关注公众号有惊喜【部分付费文章免费阅读,持续更新中…】
点击公众号底部【逆向杂记】可见
文件1
(A)描述此二进制文件提供的服务,不需要运行二进制文件来找出这一点。
(B)有可能对此二进制文件进行缓冲区溢出攻击,它在哪里?哪些输入将触发它,并且攻击者可能会使用此溢出来做什么?
文件名1
格式:x86
逆向分析1
文件功能逆向
用32位ida加载文件后,反编译main函数,如下
int __cdecl main(int argc, const char **argv, const char **envp)
{char Buffer; // [esp+5Ch] [ebp-2004h]int UserInput; // [esp+205Ch] [ebp-4h]UserInput = 0;puts("Usage:\n list - list all public keys\n view <email> - view a singular entry\n exit - leave the program\n");while ( 1 ){UserInput = readline((int)"> ");if ( !UserInput )break;if ( *(_BYTE *)UserInput ){if ( !strncmp("exit", (const char *)UserInput, 5u) )return 0;if ( !strncmp("list", (const char *)UserInput, 5u) ){list();}else if ( !strncmp("view ", (const char *)UserInput, 5u) ){view((char *)(UserInput + 5));}else if ( !strncmp("shell", (const char *)UserInput, 6u) && prereq == 11 ){snprintf(&Buffer,0x2000u,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",47,98,105,110,47,98,97,115,104,32,45,105,32,50,62,38,49);system(&Buffer);}else{prereq = 3;puts("invalid command!");}add_history(UserInput);free((void *)UserInput);UserInput = 0;}}return 0;
}
可以看出,程序首先是提示用户输入,然后是if…else if…else分支语句进行判断。如果输入的字符串不是list、view、exit等,则给prereq变量赋值为3,且打印信息"invalid command!",然后将输入的内容保存到历史输入清单中(add_history
),即
(1)若没有输入任何信息,则直接退出循环,返回0,程序退出;
(2)若输入字符串”exit”,则返回0,程序退出;
(3)若输入字符串”list”,则调用list函数;
- 反编译list函数,如下
int list()
{int result; // eaxchar list_UserInput; // [esp+1Ch] [ebp-200Ch]FILE *stream; // [esp+201Ch] [ebp-Ch]stream = popen("gpg --list-keys", "r");while ( fgets(&list_UserInput, 0x2000, stream) )fputs(&list_UserInput, stdout);if ( ferror(stream) )perror("fgets()");pclose(stream);result = prereq + 5;prereq += 5;return result;
}
list函数:将文件gpg --list-keys中的内容读入到缓冲区list_UserInput中,读入长度为0x2000,返回值为prereq+5,且令prereq的值加5。
(4)若输入字符串”view”,则调用view函数;
- 反编译view函数,如下
int __cdecl view(char *s2)
{int result; // eaxchar s; // [esp+2Ch] [ebp-400Ch]char v3; // [esp+202Ch] [ebp-200Ch]FILE *stream; // [esp+402Ch] [ebp-Ch]if ( isemail(s2) ){if ( !strncmp("XXXX@XXXX", s2, 0x1Au) ) //XXXX@XXXX表示邮箱地址,隐掉了prereq *= 2;elseprereq = 3;snprintf(&s, 0x2000u, "%s %s", "gpg --list-keys", s2);stream = popen(&s, "r");while ( fgets(&v3, 0x2000, stream) )fputs(&v3, stdout);if ( ferror(stream) )perror("fgets()");result = pclose(stream);}else{prereq = 3;result = puts("no valid email given!");}return result;
}
view函数:
-
用户输入的第5个字符开始,判断是否为邮箱地址,如果为邮箱地址,则再判断邮箱是否为 XXXX@XXXX,如果是该邮箱,则prereq乘以2,否则prereq赋值为3。
-
格式化字符串” gpg --list-keys ”+邮箱地址,打开以该字符串为文件名的文件,读取其中内容到缓冲区。
-
如果不是email,则prereq=3。
(5)若输入字符串”shell”,且prereq变量值为11,则调用snprintf函数将一串指令格式化字符串变量中,然后调用system函数执行该指令。
(6)若输入的字符串均不是以上步骤中的字符串,则赋值prereq为3,并打印”invalid command!”
缓冲区溢出漏洞
main函数中变量Buffer的堆栈地址如下:
-00002004 Buffer db ?
-00002003 db ? ; undefined
-00002002 db ? ; undefined
-00002001 db ? ; undefined
-00002000 db ? ; undefined
-00001FFF db ? ; undefined
-00001FFE db ? ; undefined
-00001FFD db ? ; undefined
......
list函数中list_UserInput的堆栈地址如下:
-0000200C list_UserInput db ?
-0000200B db ? ; undefined
-0000200A db ? ; undefined
-00002009 db ? ; undefined
-00002008 db ? ; undefined
-00002007 db ? ; undefined
-00002006 db ? ; undefined
-00002005 db ? ; undefined
-00002004 db ? ; undefined
-00002003 db ? ; undefined
view函数中viewBuffer的堆栈地址如下:
-0000200C viewBuffer db ?
-0000200B db ? ; undefined
-0000200A db ? ; undefined
-00002009 db ? ; undefined
-00002008 db ? ; undefined
-00002007 db ? ; undefined
-00002006 db ? ; undefined
-00002005 db ? ; undefined
-00002004 db ? ; undefined
-00002003 db ? ; undefined
-00002002 db ? ; undefined
攻击点在缓冲区0x200C地址处,触发思路如下:
(1)利用list、view等函数的输入,使得prereq的值改为11;且在文件gpg --list-keys文件和以gpg --list-keys + email为文件名的文件中放入需要执行的代码,前提是算好文件中指令执行的起点。
(2)在主界面输入”shell”,即可执行文件中的指令,而文件是可以人为改变的,从而达到某些特殊的目的。具体的样例因为时间就不仔细推算了。
文件2
此二进制文件检查密码,并允许缓冲区溢出。
(A)分析该二进制文件并查找需要输入的密码,解释该二进制文件如何检查密码,并说明密码是什么。您不需要运行此二进制文件。
(B)此二进制文件可能受到缓冲区溢出攻击,在哪里?什么输入会触发它,攻击者可以利用这个溢出做什么?
文件名:file2xjfk95
格式:x64
逆向分析2
文件功能逆向
用64位IDA加载文件,反编译main函数,如下
int __cdecl main(int argc, const char **argv, const char **envp)
{char UserInput[28]; // [rsp+10h] [rbp-20h]int i; // [rsp+2Ch] [rbp-4h]if ( argc > 1 )processFlags(argv[1], argv, envp);puts("Enter password:");fgets(UserInput, 999, _bss_start);for ( i = 8; i >= 0; --i ){if ( UserInput[i] != a[8 - i] ){puts("Wrong");exit(0);}}return puts("Well done, that is the correct password");
}
UserInput和缓冲区a的关系是:
UserInput[i]=a[8-i]
i从8递减至0,共9位;
循环检查,如果不满足该关系则打印”Wrong”,并退出。查看缓冲区a,如下
.data:0000000000601060 a db 'B'
.data:0000000000601061 aA3lWhgf db 'A3l*whgf',0
由此可推算出正确密码 :fghw*l3AB
缓冲区溢出漏洞
缓冲区溢出攻击在堆栈中,堆栈如下:
-0000000000000030 var_30 dq ?
-0000000000000028 db ? ; undefined
-0000000000000027 db ? ; undefined
-0000000000000026 db ? ; undefined
-0000000000000025 db ? ; undefined
-0000000000000024 var_24 dd ?
-0000000000000020 UserInput db 28 dup(?)
-0000000000000004 looptimes dd ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
可以看到,UserInput缓冲区大小为0x1c=28
个字节,后面紧跟着的4个字节存储的是循环次数(looptimes),而fgets获得输入的字符串长度为999
,因此可以在此处进行缓冲区溢出攻击,用户任意输入28个字符,再接着输入
\xff\xff\xff\xff\xff\xff\xff\xff
(即将循环次数置为-1)
那么程序执行后for循环一开始便获得i的值为-1,不满足循环条件,即不执行循环体,直接执行最后一行,打印:“Well done, that is the correct password”
即攻击者可利用这个溢出直接绕过密码验证阶段,达到特殊的目的。
攻击输入样例:
0123456789012345678901234567\xff\xff\xff\xff\xff\xff\xff\xff