CTF中關於md5的一些總結

2021-03-02 ClickJacking
前言

最近打了挺多ctf,碰到挺多關於md5的一些問題,或者一些變種的題目,雖然已經是爛大街的問題了,但是還是需要總結一下,方便下次比賽可以直接用腳本

CTF中的一些案例案例1——ciscn2020初賽——easytrick

<?php
class trick{
    public $trick1;
    public $trick2;
    public function __destruct(){
        $this->trick1 = (string)$this->trick1;
        if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
            die("你太長了");
        }
        if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
            echo file_get_contents("/flag");
        }
    }
}
highlight_file(__FILE__);
unserialize($_GET['trick']);

這裡我們拋開反序列化不談,這題本質上需要我們構造trick1=NAN,trick2=NAN即可繞過。原理也很簡單,NaN與所有值都不相等,包括它自己。當然也可以用INF繞過,原理類似,這裡不過多贅述

案例2——強網杯2020——Funhash

<?php
include 'conn.php';
highlight_file("index.php");
//level 1
if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
    die('level 1 failed');
}

//level 2
if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3']))
{
    die('level 2 failed');
}

//level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
$result = $mysqli->query($query);
$row = $result->fetch_assoc(); 
var_dump($row);
$result->free();
$mysqli->close();

這裡重點關注level 1,其他兩關都是老生常談的東西,無須過多贅述。這裡我已經寫好了腳本,下次遇到這種md4的題目直接爆破幹它

下面給出我已經爆破好的例子:

0e251288019
0e898201062

exp.php

<?php
$payload = "0123456789";
function calc_md4($s){
    $s = "0e".$s;
    $md4 = hash("md4", $s);
    if (substr($md4, 0, 2) === "0e" && ctype_digit(substr($md4, 2))) {
        echo $s.PHP_EOL;
    }
}

function getstr($payload,$s,$slen){
    if(strlen($s) == $slen){
        calc_md4($s);
        return $s;
    }
    for($i = 0;$i<strlen($payload);$i++){

        $sl = $s.$payload[$i];
        getstr($payload, $sl, $slen);
    }

}

//字符串長度從3到30,肯定找得到
for($i = 3;$i<30;$i++){
    getstr($payload,'',$i);
}

案例3——NJUPT2019南郵校賽——easyphp

這裡我截取了一部分代碼,用作案例講解

$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];
//2nd
if(is_numeric($string_1)){
    $md5_1 = md5($string_1);
    $md5_2 = md5($string_2);
    if($md5_1 != $md5_2){
        $a = strtr($md5_1, 'cxhp', '0123');
        $b = strtr($md5_2, 'cxhp', '0123');
        if($a == $b){
            echo '2nd ok'."<br>";
        }
        else{
            die("can u give me the right str???");
        }
    } 
    else{
        die("no!!!!!!!!");
    }
}

這裡的關鍵就是,需要我們找兩個個是ce開頭的,然後ce後面是純數字的md5值

下面給出我已經爆破好的例子:

N9KU3
QlYRH

爆破的腳本如下

exp.py

import string
import hashlib
payload = string.ascii_letters+string.digits
def calc_md5(s):
    md5 = hashlib.md5(s.encode("utf-8")).hexdigest()
    if (md5[0:2] == "ce" and md5[2:].isdigit()) :
        print(s)

def getstr(payload,s,slen):
    if(len(s) == slen):
        calc_md5(s)
        return s

    for i in payload:
        sl = s+i
        getstr(payload, sl, slen)

# 字符串長度從0到30,肯定找得到
for i in range(3,30):
    getstr(payload,'',i)

案例4——雙MD5碰撞繞過

<?php

if (isset($_GET['a']) && isset($_GET['b'])) {
    $a = $_GET['a'];
    $b = $_GET['b'];
    if ($a != $b && md5($a) == md5(md5($b))) {
        echo "flag{XXXXX}";
} else {
        echo "wrong!";
    }

} else {
    echo 'wrong!';
}
?>

這裡其實我們只要找出md5(md5($b))是0e開頭的且0e後面是純數字的字符串即可

下面給出我已經爆破好的例子:

f2WfQ
iv2Cn

爆破腳本如下:

exp.py

import string
import hashlib

payload = string.ascii_letters + string.digits


def calc_md5(s):
    md5 = hashlib.md5(s.encode("utf-8")).hexdigest()
    md5_double = hashlib.md5(md5.encode("utf-8")).hexdigest()
    if (md5_double[0:2] == "0e" and md5_double[2:].isdigit()):
        print(s)


def getstr(payload, s, slen):
    if (len(s) == slen):
        calc_md5(s)
        return s

    for i in payload:
        sl = s + i
        getstr(payload, sl, slen)


# 字符串長度從0到30,肯定找得到
for i in range(3, 30):
    getstr(payload, '', i)

案例5——php中md5($str,true)注入

<?php
    $password = $_POST['password'];
    $sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
    $result = mysqli_query($link,$sql);
    if(mysqli_num_rows($result)>0){
        echo 'Success';
    }else{
        echo 'Failure';
    }
?>

這裡md5輸出的是16 字符二進位格式,並不是我們平時看到的32字符十六進位數,所以我們只需要找md5加密後字符串中是否存在'or'字符串

下面給出我已經爆破好的例子:

ffifdyop
4SV7p
bJm4aG
bNas5p
ckHAEb

exp.php

<?php
$payload = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
function calc_md5_true($s)
{
    $md5_true = md5($s,true);
    if (strpos($md5_true,"'or'") !== false){
        echo $s.PHP_EOL;
    }
}

function getstr($payload, $s, $slen)
{
    if (strlen($s) == $slen) {
        calc_md5_true($s);
        return $s;
    }
    for ($i = 0; $i < strlen($payload); $i++) {

        $sl = $s . $payload[$i];
        getstr($payload, $sl, $slen);
    }

}

//字符串長度從3到30,肯定找得到
for ($i = 3; $i < 30; $i++) {
    getstr($payload, '', $i);
}

案例6——強網杯2018——Web 籤到

這裡選取了部分代碼,用作演示

<?php
if((string)$_POST['param1']!==(string)$_POST['param2'] && md5($_POST['param1'])===md5($_POST['param2'])){
    die("success!");
}

這裡因為對兩個參數都進行了強制類型轉換,所以一般的方法(用數組報錯繞過肯定是行不通的了),所以我們必須找到兩個文件,他們的內容不一樣,但是md5值相等

在網上找到兩張圖片,他們內容不一樣,但是md5值一樣

double_md5_1double_md5_2

exp.py

# -*- coding: utf-8 -*-
import requests as req
url = 'http://localhost:82/WWW/test2.php'
with open('double_md5_1.jpg','rb') as f:
    md51 = f.read()
with open('double_md5_2.jpg','rb') as f:
    md52 = f.read()

data = {'param1':md51,'param2':md52}
r = req.post(url=url,data=data)
print(r.text)

案例7——md5($a)與md5(md5($a))都是0e開頭的字符串

下面給出我已經爆破好的例子:


exp.py

import string
import hashlib

payload = string.ascii_letters + string.digits


def calc_md5(s):
    md5 = hashlib.md5(s.encode("utf-8")).hexdigest()
    md5_double = hashlib.md5(md5.encode("utf-8")).hexdigest()
    if (md5[0:2] == "0e" and md5[2:].isdigit() and md5_double[0:2] == "0e" and md5_double[2:].isdigit()):
        print(s)


def getstr(payload, s, slen):
    if (len(s) == slen):
        calc_md5(s)
        return s

    for i in payload:
        sl = s + i
        getstr(payload, sl, slen)


# 字符串長度從0到30,肯定找得到
for i in range(3, 30):
    getstr(payload, '', i)

拓展三張圖片碰撞

在上個案例中,只是兩張圖片碰撞。這裡我們思考一個問題,如果是三張圖片呢,是否存在?答案是存在的

three_md5_1three_md5_2three_md5_3兩個其他類型的文件md5碰撞

首先下載hashclash,如果是linux環境的話可以下載編譯好的二進位文件,如果是macos系統的話,就需要下載源碼,然後自行編譯,編譯成功如下:

image-20200828160628187創建目錄

mkdir ipc_workdir

cd ipc_workdir

運行腳本

echo -n "TEST" > prefix.txt

../scripts/poc_no.sh prefix.txt

稍等片刻後,就可以碰撞出來了

image-20200828164834105php文件的md5碰撞

下載文件:

wget https://s3-eu-west-1.amazonaws.com/md5collisions/a.php

wget https://s3-eu-west-1.amazonaws.com/md5collisions/b.php

image-20200828205602032

How I made two PHP files with the same MD5 hash

sha1碰撞

下載文件

curl -sSO https://shattered.it/static/shattered-1.pdf

curl -sSO https://shattered.it/static/shattered-2.pdf

image-20200828173329579任意多個文件,文件內容不同,md5值相同

運用的原理如下:

MD5(file + col1_a + col2_a) === MD5(file + col1_a + col2_b) === MD5(file + col1_b + col2_a) === MD5(file + col1_b + col2_b)

這裡使用的工具還是hashclash

我們在在hashclash文件夾下創建工作目錄,然後執行下面的shell腳本

gen.sh

../bin/md5_fastcoll test.txt -o test_1.txt test_2.txt
../bin/md5_fastcoll test_1.txt -o test_1_1.txt test_1_2.txt
../bin/md5_fastcoll test_1_1.txt -o test_1_1_1.txt test_1_1_2.txt
./genfiles.py

genfiles.py

with open("test.txt","rb") as f:
    base = f.read()

with open("test_1.txt","rb") as f:
    file_1 = f.read()
    coll_1 = file_1[len(base):]

with open("test_2.txt","rb") as f:
    file_2 = f.read()
    coll_2 = file_2[len(base):]

with open("test_1_1.txt","rb") as f:
    file_1_1 = f.read()
    coll_1_1 = file_1_1[len(file_1):]

with open("test_1_2.txt","rb") as f:
    file_1_2 = f.read()
    coll_1_2 = file_1_2[len(file_2):]


with open("test_1_1_1.txt","rb") as f:
    file_1_1_1 = f.read()
    coll_1_1_1 = file_1_1_1[len(file_1_1):]

with open("test_1_1_2.txt","rb") as f:
    file_1_1_2 = f.read()
    coll_1_1_2 = file_1_1_2[len(file_1_2):]


def w(fn, data):
    f = open(fn, "wb")
    f.write(data)
    f.close()

w("test1.txt", base + coll_1 + coll_1_1 + coll_1_1_1)
w("test2.txt", base + coll_1 + coll_1_1 + coll_1_1_2)
w("test3.txt", base + coll_1 + coll_1_2 + coll_1_1_1)
w("test4.txt", base + coll_1 + coll_1_2 + coll_1_1_2)
w("test5.txt", base + coll_2 + coll_1_1 + coll_1_1_1)
w("test6.txt", base + coll_2 + coll_1_2 + coll_1_1_1)

這樣就可以生成6個文件內容不同,但是它們的md5都相同,事實上,你可以生成任意多個

總結

其實很多題目都是類似的,需要我們自己動手寫腳本,本質上就是去找hash加密後的字符串前兩位是0e,後30位是純數字的,只要會寫腳本,就是萬變不離其宗

Reference

MD5碰撞的一些例子

How to make two binaries with the same MD5 hash

How I made two PHP files with the same MD5 hash

Are there two known strings which have the same MD5 hash value?

SHA1 collision demo / example

ebctf 2013: MD5 COLLIDING

PHP中MD5碰撞Bypass

相關焦點

  • CTF題記——計劃第一周
    m0re不急著查列,先看看ctf表中是什麼,m0reHardSQL進行測試,測試閉合符號和一些常見過濾。首先測試閉合符號,輸入a'來進行測試== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); }}?><html><style> body{ background:url(.
  • CTF系列 1 密碼題解密網站總結(必收藏乾貨)
    ,都能看到密碼學的影子,所以掌握必要的密碼學知識尤為重要,上文我整理分享了一些常見的加密方式,但略去了很多變種的加密方式,此間便做一個補充,也是方便在線解題,儘可能省去臨時查找在線解密網站的時間。0x01 切入正題0.綜合性網站網站中包含大多編碼的解碼。
  • md5到md5破解的一些科普
  • CTF中常見的PHP漏洞小結
    在做ctf題的時候經常會遇到一些PHP代碼審計的題目,這裡將我遇到過的常見漏洞做一個小結。md5()漏洞  PHP在處理哈希字符串時,會利用」!=」或」==」來對哈希值進行比較,它把每一個以」0E」開頭的哈希值都解釋為0,所以如果兩個不同的密碼經過哈希以後,其哈希值都是以」0E」開頭的,那麼PHP將會認為他們相同,都是0。
  • Python 中 MD5 加密
    # 由於MD5模塊在python3中被移除# 在python3中使用hashlib模塊進行md5操作import hashlib# 待加密信息str = '123456'# 創建md5對象hl = hashlib.md5()#更新hash對象的值,如果不使用update方法也可以直接md5構造函數內填寫
  • CTF中的PHP安全特性總結
    if (md5($_POST['a']) === md5($_POST['b']))  5.             echo $flag;  6.         else  7.             print 'Wrong.
  • 【代碼審計】PHP代碼審計之CTF系列(3)
    payload:http://127.0.0.1/ctf/1.php?file=http://127.0.0.1//ctf/1.php?如果您還想在 include_path(在 php.ini 中)中搜索文件的話,請設置該參數為 '1'。context 可選。規定文件句柄的環境。context 是一套可以修改流的行為的選項。若使用 NULL,則忽略。start 可選。規定在文件中開始讀取的位置。該參數是 PHP 5.1 中新增的。max_length 可選。
  • CTF從入門到提升(三)
    3.從基礎題出發 一般都是100,200,最高分在500,600 先把100分的學好,可從實踐,高中的ctf學起,比較簡單,只涉及1,2個點PWN、Reverse偏重對彙編、逆向的理解 對底層理解;Crypto偏重對數學、算法的深入學習 密碼課要深入學;Web偏重對技巧沉澱、快速搜索能力的挑戰 發散思維,對底層只需要了解,代碼原理,關於漏洞點的積累
  • Python 中 MD5 哈希函數的實現
    Python中的MD5哈希與md5相關的功能示例1:在Python中列印等效於MD5哈希的字節示例2:在Python中列印MD5哈希的十六進位等效項示例3:Python MD5文件校驗輸出與說明示例4:使用Python在MD5中編碼字符串輸出與說明示例5:在Python中計算文件的MD5哈希
  • CTF入門指南 | 內附教程分享
    1.分析賽題情況2.分析自身能力 自己最適合哪個方向3.選擇更適合的入手分析賽題PWN、Reverse偏重對彙編、逆向的理解 對底層理解Crypto偏重對數學、算法的深入學習 密碼課要深入學Web偏重對技巧沉澱、快速搜索能力的挑戰 發散思維,對底層只需要了解,代碼原理,關於漏洞點的積累
  • MD5 簡介,及其在 Java 中的實現方式
    比如,客戶端與伺服器的 HTTP 通信,通信雙方可以將報文內容做一個 MD5 計算,並將計算所得 MD5 值一併傳遞給彼此,這樣,接收方可以通過對報文內容再次做 MD5 計算得到一個 MD5 值,與傳遞報文中的 MD5 值做比較,驗證數據是否完整,或者是否中途被攔截篡改過。再比如,網絡雲盤中的文件秒傳功能也運用到 MD5 算法。
  • PHP弱類型比較與md5繞過
    那麼php弱類型這裡的「弱」,指的不是某個類型有什麼問題,而是整個php語言中某些函數在處理賦值、字符串比較、變量比較的過程中對類型似乎並不關心,弱化了類型帶來的影響,在使用某個變量時不需要我們定義變量的類型,而是根據內容判斷這是什麼類型,從而導致了各種漏洞的發生。例如:<?
  • php代碼審計總結
    "白帽子社區在線CTF靶場BMZCTF,歡迎各位在這裡練習、學習,BMZCTF全身心為網絡安全賽手提供優質學習環境,連結(http://www.bmzclub.cn/)"    在php中可由用戶輸入的變量
  • Excel~VBA實現MD5加密
    網絡上關於這段代碼的文章很多,但是注釋及源碼出處不詳。這裡我也沒找到原始出處,只對代碼做了簡單的注釋。
  • CTF小白入門學習指南
    既然是入門的帖子,那我們就先來介紹一下CTF的一些基本的概念。
  • 61【Linux】一步一步學Linux——md5sum命令(61)
    如下文本文件,無論通過哪種模式讀取md5都一致。4.6 重定向追加這裡新增文件ls,單獨求其md5,將其md5追加到文件中校驗時,根據已生成的md5來進行校驗。生成當前文件的md5,並和之前已經生成的md5進行對比,如果一致,則返回OK,否則返回錯誤信息 [deng@localhost test]$ md5sum -c d.md5  passwd: 確定 passwd1: 確定 passwd.md5:
  • MD5足夠嗎----談PHP中信息加密技術
    同樣是一道面試答錯的問題,面試官問我非對稱加密算法中有哪些經典的算法?當時我愣了一下,因為我把非對稱加密與單項散列加密的概念弄混淆了,所以更不用說什麼非對稱加密算法中有什麼經典算法,結果當然也讓面試官愣了一下,所以今天就花點時間說說PHP中的信息加密技術信息加密技術的分類單項散列加密技術(不可逆的加密)屬於摘要算法,不是一種加密算法,作用是把任意長的輸入字符串變化成固定長的輸出串的一種函數MD5string md5 ( string $
  • 【網安帶專測評】md5bypass靶場八關
    分析:$_GET["pass"] != hash("md4", $_GET["pass"]!=是弱類型比較,意思要找到一個值,hash函數之後的值等於原值,即md4加密前後相等,才能繞過。可以使用科學計數法0e進行繞過:通過md4碰撞,得到特定的字符串(科學計數法表示)加密的結果同為科學計數法表示。這一關的科學計數法所表示的字符串後面不能包含字母而應該是純數字。結果:給出兩個加密之後開頭仍是0e的字符串:
  • 一種基於Md5算法的改進加密方法
    【51CTO.com 獨家特稿】本文討論了Md5加密算法在應用方面遇到的一些問題,並且針對這種問題在基於Md5算法的基礎上改進了加密方法,從而使用Md5加密口令方面更加安全。一些在線的MD5值查詢網站提供MD5密碼值的查詢,輸入MD5密碼值後,如果在資料庫中存在,那麼可以很快獲取其密碼值。(2)使用MD5破解工具。網絡上有許多針對MD5破解的專用軟體,通過設置字典來進行破解。(3)通過社會工程學來獲取或者重新設置用戶的口令。
  • 淺談md5弱類型比較和強碰撞
    由於1.txt和2.txt文件中含有不可見字符,所以需要將其url編碼後使用。可以看到url編碼後的兩個字符串不完全相同,滿足我們輸入兩個不同參數的需要。>首先查看一些strtr()函數的用法:strtr() 函數轉換字符串中特定的字符。