天天看點

第一次php代碼審計

1.源代碼

<?php

require_once('flag.php');
error_reporting(0);


if(!isset($_GET['msg'])){
    highlight_file(__FILE__);
    die();
}

@$msg = $_GET['msg'];
if(@file_get_contents($msg)!=="Hello Challenge!"){//php://input繞過
    die('Wow so rude!!!!1');
}

echo "Hello Hacker! Have a look around.\n";

@$k1=$_GET['key1'];
@$k2=$_GET['key2'];

$cc = 1337;$bb = 42;

if(intval($k1) !== $cc || $k1 === $cc){//GET類型接收的參數值類型為字元串,是以1337直接繞過
    die("lol no\n");
}

if(strlen($k2) == $bb){
    if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){//這裡比較坑,$符并不表示比對輸入行尾位置,可以用url編碼測試一下
        if($k2 == $cc){
            @$cc = $_GET['cc'];
        }
    }
}

list($k1,$k2) = [$k2, $k1];

if(substr($cc, $bb) === sha1($cc)){//利用substr和sha1傳回FALSE繞過
    foreach ($_GET as $lel => $hack){
        $$lel = $hack;//對k1變量覆寫
    }
}

$‮b = "2";$a="b";//;1=b
//上面一行代碼是$b="2";$a="b";//$b=1
if($$a !== $k1){//經過上面$$a值為2,是以我們可以為k1變量覆改為2
    die("lel no\n");
}

// plz die now
assert_options(ASSERT_BAIL, 1);
assert("$bb == $cc");//assert()将字元串參數當做php代碼執行

echo "Good Job ;)";
// TODO
// echo $flag;   
?>
           

1.)首先利用php://input僞協定繞過(@file_get_contents($msg)!=="Hello Challenge!")

1.1)file_get_concents() 函數把整個檔案讀入一個字元串中

文法 : file_get_contents(path,include_path,context,start,max_length)

參數 描述
path 必需。規定要讀取的檔案。
include_path 可選。如果也想在 include_path 中搜尋檔案的話,可以将該參數設為 "1"。
context

可選。規定檔案句柄的環境。

context 是一套可以修改流的行為的選項。若使用 null,則忽略。

start 可選。規定在檔案中開始讀取的位置。該參數是 PHP 5.1 新加的。
max_length 可選。規定讀取的位元組數。該參數是 PHP 5.1 新加的
例子
           
<?php echo file_get_contents("test.txt"); ?>
           
輸出:This is a test file with test text.
           

1.2)php://input

當對file_get_contents傳進去的參數作為檔案名變量去打開檔案時,可以将參數php://傳進,同時post方式傳進去值作為檔案内容,供php代碼執行時當做檔案内容讀取

2.)intval和===

2.1)intval函數擷取變量整數數值

Intval 最大的值取決于作業系統。 32 位系統最大帶符号的 integer 範圍是 -2147483648 到 2147483647。舉例,在這樣的系統上, intval(‘1000000000000’) 會傳回 2147483647。64 位系統上,最大帶符号的 integer 值是 9223372036854775807。這個有個應用就是在判斷數值是不是回文上,如果參數為2147483647,那麼當它反過來,由于超出了限制,是以依然等2147483647。即為回文。當然這裡用不上

intval — 擷取變量的整數值

int intval ( mixed

$var

[, int

$base

= 10 ] )

通過使用指定的進制

base

轉換(預設是十進制),傳回變量

var

的 integer 數值。 intval() 不能用于 object,否則會産生

E_NOTICE

錯誤并傳回 1。

var

要轉換成 integer 的數量值

base

轉化所使用的進制

Note:

如果

base

是 0,通過檢測

var

的格式來決定使用的進制:
  • 如果字元串包括了 "0x" (或 "0X") 的字首,使用 16 進制 (hex);否則,
  • 如果字元串以 "0" 開始,使用 8 進制(octal);否則,
  • 将使用 10 進制 (decimal)。

成功時傳回

var

的 integer 值,失敗時傳回 0。 空的 array 傳回 0,非空的 array 傳回 1。

最大的值取決于作業系統。 32 位系統最大帶符号的 integer 範圍是 -2147483648 到 2147483647。舉例,在這樣的系統上, intval('1000000000000') 會傳回 2147483647。64 位系統上,最大帶符号的 integer 值是 9223372036854775807。

字元串有可能傳回 0,雖然取決于字元串最左側的字元。 使用 整型轉換 的共同規則。

2.2)

===是恒等計算符   同時檢查表達式的值與類型

==是比較運算符号  不會檢查條件式的表達式的類型(弱類型,類型自動轉換)

這裡因為從$_GET接收的參數類型都是String(本地可以測試一下),是以輸入1337可以直接繞過

3.)因為代碼中的$不表示比對輸入行尾,是以這裡可以進行正則比對繞過。

其次is_numeric()  判斷變量是否為數字或數字字元串,不僅檢查10進制,16進制是可以。

is_numeric函數對于空字元%00,無論是%00放在前後都可以判斷為非數值,而%20空格字元隻能放在數值後。是以,檢視函數發現該函數對對于第一個空格字元會跳過空格字元判斷,接着後面的判斷!該函數還可能造成sql注入,例如将‘1 or 1'轉換為16進制形式,再傳參,就可以造成sql注入。

當然此處用不到,因為可以構造key2=1337$aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa進行繞過

4.)利用substr和sha1函數同時傳回FALSE繞過

4.1)

substr — 傳回字元串的子串

string substr ( string

$string

, int

$start

[, int

$length

] )

傳回字元串

string

start

length

參數指定的子字元串。

string

輸入字元串。必須至少有一個字元。

start

如果

start

是非負數,傳回的字元串将從

string

start

位置開始,從 0 開始計算。例如,在字元串 "abcdef" 中,在位置 0 的字元是 "a",位置 2 的字元串是 "c" 等等。

如果

start

是負數,傳回的字元串将從

string

結尾處向前數第

start

個字元開始。

如果

string

的長度小于

start

,将傳回

FALSE

。(這裡用到的是這個)

length

如果提供了正數的

length

,傳回的字元串将從

start

處開始最多包括

length

個字元(取決于

string

的長度)。

如果提供了負數的

length

,那麼

string

末尾處的

length

個字元将會被省略(若

start

是負數則從字元串尾部算起)。如果

start

不在這段文本中,那麼将傳回

FALSE

如果提供了值為 0,

FALSE

NULL

length

,那麼将傳回一個空字元串。

如果沒有提供

length

,傳回的子字元串将從

start

位置開始直到字元串結尾。

4.2)sha1()函數無法處理數組類型,将報錯并傳回FALSE。經驗證md5()函數同樣存在此漏洞。

5.下面就是變量覆寫了

由代碼$b="2";$a="b";可以知道$$a的值為2,是以我們可以為k1變量覆改為2,以此繞過$$a !== $k1

6.在下面就是指令執行了(利用assert函數)

将b利用變量覆寫為system("vim ./flag.php");//就可以通路flag.php檔案了,即可發現flag