<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    【技術分享】區塊鏈CTF OJ平臺ChainFlag -EVMEnc Writeup

    VSole2021-10-21 16:21:52

    ChainFlag是一個區塊鏈主題的CTF OJ平臺,個人感覺現有題目質量很高,值得一做,這里分享下自己做題的過程。

    EVMEnc

    題目簡介

    題目提示簡單的EVM加密,給了兩個附件,info.txt與source.sol,附件如下圖所示:

    info.txt

    transaction1.0x81200224..................2.0xffdd8131000000000000000000000000000000000000000000003100e35e552c1273c959sload(0) = 2086453827893285425773093.0xffdd8131000000000000000000000000000000000000000000001ac3243c9e81ba850045sload(0) = 293410643427570933331044.0xffdd8131000000000000000000000000000000000000000000005ce6a91010e307946b87sload(0) = 2271039174494515057851925.0xe6dc28ae..................sload(3) = 1970527074032043059410457910532573615730510348629701619382
    

    source.sol

    pragma solidity ^0.5.10;
    contract EVMEnc {
        uint public result;    string public key;
        uint private delta;    uint public output;
        uint32 public sum;    uint224 private tmp_sum=0;
        uint32 private key0;    uint224 private t0=0;    uint32 private key1;    uint224  private t1=0;    uint32 private key2;    uint224 private  t2=0;    uint32  private key3;    uint224 private  t3=0;
        constructor() public {        delta = 0xb3c6ef3720;    }
        function Convert(string memory source) public pure returns (uint result) {        bytes32 tmp;        assembly {            tmp := mload(add(source, 32))        }        result = uint(tmp) / 0x10000000000000000;    }
        function set_key(string memory tmp) public {        key = tmp;    }
        function cal_(uint x) public {        uint tmp = Convert(key) / 0x10000000000000000;        result = tmp % x;    }
        function Encrypt(string memory flag) public {        uint tmp = Convert(flag);        uint key_tmp = Convert(key) / 0x10000000000000000;        assembly {            let first,second            sstore(5, and(shr(96, key_tmp), 0xffffffff))            sstore(6, and(shr(64, key_tmp), 0xffffffff))            sstore(7, and(shr(32, key_tmp), 0xffffffff))            sstore(8, and(key_tmp, 0xffffffff))
                let step := 1            for { let i := 1 } lt(i, 4) { i := add(i, 1) } {
                    first := and(shr(mul(add(sub(24, mul(i, 8)), 4), 8), tmp), 0xffffffff)                second := and(shr(mul(sub(24, mul(i, 8)), 8), tmp), 0xffffffff)
                    sstore(4, 0)
                    for {let j := 0 } lt(j, 32) { j := add(j, 1) } {
                        sstore(4, and(add(and(sload(4), 0xffffffff), shr(5, sload(2))), 0xffffffff))
                        let tmp11 := and(add(and(mul(second, 16), 0xffffffff), and(sload(5), 0xffffffff)), 0xffffffff)                    let tmp12 := and(add(second, and(sload(4),0xffffffff)), 0xffffffff)                    let tmp13 := and(add(div(second, 32), and(sload(6),0xffffffff)), 0xffffffff)
                        first := and(add(first, xor(xor(tmp11, tmp12), tmp13)), 0xffffffff)
                        let tmp21 := and(add(and(mul(first, 16), 0xffffffff), and(sload(7),0xffffffff)), 0xffffffff)                    let tmp22 := and(add(first, and(sload(4),0xffffffff)), 0xffffffff)                    let tmp23 := and(add(div(first, 32), and(sload(8),0xffffffff)), 0xffffffff)                    second := and(add(second, xor(xor(tmp21, tmp22), tmp23)), 0xffffffff)
                    }
                    sstore(3, add(sload(3), add(shl(sub(192, mul(step, 32)), first), shl(sub(192, mul(i, 64)), second))))                step := add(step, 2)            }
            }    }}
    

    題目分析

    題目提供的兩個附件source.sol為智能合約源碼,info.txt為具體的交易序列,包含了交易的輸入數據以及執行后部分存儲storage的狀態。利用remix編譯智能合約,在Compilation Details中查看Functionhashes如下圖所示:

    分析info.txt文件,文件給出了5條交易的部分輸入信息以及部分執行后的狀態。以第一條為例:

    1.0x81200224..................
    

    給出了交易發生的輸入的前4個字節為0x81200224,交易輸入的前4個字節一般對應了調用方法的哈希,后面的輸入為調用方法使用的參數,這里調用了方法set_key(string),但是隱去了string參數的的具體內容。

    分析第二條交易信息:

    2.
    0xffdd8131000000000000000000000000000000000000000000003100e35e552c1273c959
    sload(0) = 208645382789328542577309
    

    對應交易的方法為cal_(unit256),參數為0x3100e35e552c1273c959。sload(0)返回的是storage[0]的信息,根據合約對應的是全局變量result,意思是執行完交易后,result=208645382789328542577309

    對info.txt中的信息繼續處理,結果如下所示:

    transaction1.set_key(string memory tmp)2.cal_(uint 0x3100e35e552c1273c959)  result = 0x2c2eb0597447608b329d3.cal_(uint 0x1ac3243c9e81ba850045)  result = 0x6369510a41dbcbed8704.cal_(uint 0x5ce6a91010e307946b87)  result = 0x301753fa0827117d19685.Encrypt(string memory flag)output =0x505d433947f27742f60b06f350f2583450a1f7221380eeb6
    

    到這里題目的基本要求和大題思路算是比較清楚了,題目算是一個利用solidity語言構造的一個加解密題目,題目設置未知的key值,然后告知調用三次cal_函數的結果,之后要求通過flag加密后的輸出求出flag。

    下面的重點在于分析cal和encrypt函數,這兩個函數的編寫加入了內聯匯編,匯編指令的理解可以參考文檔,總體而言都是對sotrage、memory以及stack的操作。本人作為一個菜狗子主要通過以下這樣的方式來幫助理解,一是通過本地動態調試,題目給出了源碼,可以利用remix本地部署并對相關函數進行調試,二是將用python重寫函數,這樣也方便了后續的解密程序編寫。

    通過調試可知cal函數可以理解為取余,已知:

    0x2c2eb0597447608b329d = tmp % 0x3100e35e552c1273c959 0x6369510a41dbcbed870 = tmp % 0x1ac3243c9e81ba850045 0x301753fa0827117d1968 = tmp % 0x5ce6a91010e307946b87
    

    求解取余方程可以利用中國剩余定理,具體實現附在最后。

    求出了tmp后,分析Encrypt函數,先看循環前的部分:

    uint tmp = Convert(flag);        uint key_tmp = Convert(key) / 0x10000000000000000;        assembly {            let first,second            sstore(5, and(shr(96, key_tmp), 0xffffffff))            sstore(6, and(shr(64, key_tmp), 0xffffffff))            sstore(7, and(shr(32, key_tmp), 0xffffffff))            sstore(8, and(key_tmp, 0xffffffff))
    

    之前所求的tmp就是這里的key_tmp,那么存儲在storage[5]到storage[8]都是固定值可以直接求出。后續部分用到的sload(2)是取storage[2]的值,按照源碼分析對應的是變量delta=0xb3c6ef3720。storage[3]對應的output用來存儲結果,由循環部分每次循環計算的結果移位拼接而成。將Encrypt函數重寫成python,轉化過程中需要注意符號的優先級,結果如下:

    tmp = flag  #Convert(flag)  48 hexkey_tmp =0x6b65795f746869735f69735f6b65795fsstore5 = 0x6b65795f # 0x6b65795f 74686973 5f69735f 6b65795f >>96sstore6 = 0x74686973sstore7 = 0x5f69735fsstore8 = 0x6b65795fsstore2 = 0xb3c6ef3720sstore3 = 0step = 1sstore4listall = []//markfor i in range(1,4):    first = (tmp >> ((24 - i * 8)+4) * 8) & 0xffffffff    second = (tmp >> (24 - i * 8) * 8) & 0xffffffff    sstore4 = 0    sstore4list = []    for j in range(0, 32):        sstore4 = ((sstore4 & 0xffffffff) + (sstore2 >> 5)) & 0xffffffff        sstore4list.append(sstore4)//mark        tmp11 = (((second * 16) & 0xffffffff) + sstore5 ) & 0xffffffff        tmp12 = (second + sstore4) & 0xffffffff        tmp13 = ((second >> 5) + sstore6) & 0xffffffff        first = (first + (tmp11 ^ tmp12 ^ tmp13)) & 0xffffffff        tmp21 = (((first << 4) & 0xffffffff) + sstore7) & 0xffffffff        tmp22 = (first + sstore4) & 0xffffffff        tmp23 = ((first >> 5) + sstore8) & 0xffffffff        second = (second + (tmp21 ^ tmp22 ^ tmp23)) & 0xffffffff    sstore4listall.append(sstore4list)//mark    sstore3 = sstore3 + ((first << (192 - (step * 32))) + (second << (192 - (i * 64))))    step = step + 2print(hex(sstore3))#sstore3 = 0x505d433947f27742f60b06f350f2583450a1f7221380eeb6
    

    由以上這段加密過程求解flag,下面分析加密算法。算法主體是兩層循環,第一層循環了3次,tmp為24字節,第一次循環求出的first和second是tmp的高位的0-4字節以及5-8字節,后續循環每次取8字節前部分為first變量,后部分為second變量。通過第二層循環后將first與second再度拼接組合,循環三次后為最終的輸出。

    第二層循環32次,其中的sstore4為storage[4]的存儲值,初始為0并且隨著循環不斷變化,但是在3*32次的循環中與輸入的flag無關,這96個數值是固定的,這里我設立了一個數組sstore4list用來存儲記錄,方便后續的解密。第二層的循環中的tmp11,tmp12,tmp13三個變量僅與second有關,first依據這三個值變化,tmp21,tmp22,tmp23三個變量僅與first有關,second依據這三個值變化,循環32次后為最后得到后續拼接的first與second。

    依據主要邏輯可以理解為以下形式:

    tmp = [a,b,c]output =[]t = 0for i in range(0,3):    first = tmp[i][:16]    second = tmp[i][17:]    for j in range(0, 32):        tmp1 = unknow_1(second,sstore4[t])        first = (first + tmp1)& 0xffffffff        tmp2 = unknow_2(first,sstore4[t])        second = (second + tmp2)& 0xffffffff        t = t+1    output.append([first,second])
    

    現在邏輯就清晰多了,先分組后加密在組合,類似于常見流密碼的加密方式,對以上加密過程解密的邏輯可以理解為以下形式:

    tmp = []output =[a,b,c]t = 0for i in range(0,3):    first = output[i][:16]    second = output[i][17:]    for j in range(0, 32):        tmp1 = unknow_1(second,sstore4[t])        first = (first - tmp1)& 0xffffffff        tmp2 = unknow_2(first,sstore4[t])        second = (second - tmp2)& 0xffffffff        t = t+1    tmp.append([first,second])
    

    下面為了實現解密,我需要完成充實細節,比如計算出其中的用到96次的不同的sstore4,比如變量的移位拼接操作的具體實現等,實現解密即可求解出flag,實現代碼附在后面。

    完整解題過程

    中國剩余定理求解key_tmp:

    import gmpy2def crt(b,m):    for i in range(len(m)):        for j in range(i+1,len(m)):            if gmpy2.gcd(m[i],m[j]) != 1:                print("error")                return -1    M = 1    for i in range(len(m)):        M *= m[i]    Mm = []    for i in range(len(m)):        Mm.append(M // m[i])    Mm_ = []    for i in range(len(m)):        _,a,_ = gmpy2.gcdext(Mm[i],m[i])        Mm_.append(int(a % m[i]))    y = 0    for i in range(len(m)):        y += (Mm[i] * Mm_[i] * b[i])    y = y % M    return yb = [0x2c2eb0597447608b329d,0x6369510a41dbcbed870,0x301753fa0827117d1968]m = [0x3100e35e552c1273c959,0x1ac3243c9e81ba850045,0x5ce6a91010e307946b87]key = crt(b,m)print (hex(key))print (str(hex(key)[2:-1]).decode('hex'))
    

    解密代碼實現:

    sstore3 = 0x505d433947f27742f60b06f350f2583450a1f7221380eeb6key_tmp =0x6b65795f746869735f69735f6b65795fsstore5 = 0x6b65795fsstore6 = 0x74686973sstore7 = 0x5f69735fsstore8 = 0x6b65795fsstore2 = 0xb3c6ef3720tmp = 0step = 1sstore4listall = []for i in range(1,4):    sstore4 = 0    sstore4list = []    for j in range(0, 32):        sstore4 = ((sstore4 & 0xffffffff) + (sstore2 >> 5)) & 0xffffffff        sstore4list.append(sstore4)    sstore4listall.append(sstore4list)
    for i in range(1,4):    first = (sstore3 >> ((24 - i * 8)+4) * 8) & 0xffffffff    second = (sstore3 >> (24 - i * 8) * 8) & 0xffffffff    sstore4 = 0    for j in range(0, 32):        sstore4 = sstore4list[31 - j]        tmp21 = (((first << 4) & 0xffffffff) + sstore7) & 0xffffffff        tmp22 = (first + sstore4) & 0xffffffff        tmp23 = ((first >> 5 )+ sstore8) & 0xffffffff        second = (second - (tmp21 ^ tmp22 ^ tmp23)) & 0xffffffff        tmp11 = (((second << 4) & 0xffffffff) + sstore5) & 0xffffffff        tmp12 = (second + sstore4) & 0xffffffff        tmp13 = ((second >> 5) + sstore6) & 0xffffffff        first = (first - (tmp11 ^ tmp12 ^ tmp13)) & 0xffffffff    tmp = tmp + ((first << (192 - (step * 32))) + (second << (192 - (i * 64))))    step = step + 2print(hex(tmp))print (str(hex(tmp)[2:-1]).decode('hex'))
    
    ctffor循環
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    關于 DNS TXT 記錄的意義可以參考下面兩篇文章:TXT 記錄值 - Google Workspace 管理員幫助https://support.google.com/a/answer/2716802?hl=zh-HansDNS中TXT記錄是做什么用的?既然輸出沒有問題了,可以進行轉換了,這里又涉及一個問題:certutil 只能對文件進行轉換,
    它指的是一個有用的工具庫,幫助處理和操作XML格式的數據。ROME庫允許我們把XML數據轉換成Java中的對象,這樣我們可以更方便地在程序中操作數據。另外,它也支持將Java對象轉換成XML數據,這樣我們就可以把數據保存成XML文件或者發送給其他系統。
    FastJson結合二次反序列化繞過黑名單
    mruby是一個Ruby語言的輕量級實現,mruby工作方式類似CPython,它可以將Ruby源碼編譯為字節碼,再在虛擬機中解釋運行。
    前言本文主要著眼于glibc下的一些漏洞及利用技巧和IO調用鏈,由淺入深,分為 “基礎堆利用漏洞及基本IO攻擊” 與 “高版本glibc下的利用” 兩部分來進行講解,前者主要包括了一些glibc相關的基礎知識,以及低版本glibc下常見的漏洞利用方式,后者主要涉及到一些較新的glibc下的IO調用鏈。
    前言在Teaser Dragon CTF 2019中有2道考察Crypto方向的題目,一道Crypto題目,一道Web+Crypto題目,在這里對題目進行一下分析。
    ChainFlag是一個區塊鏈主題的CTF OJ平臺,個人感覺現有題目質量很高,值得一做,這里分享下自己做題的過程。
    前言隨著射頻識別技術的發展,射頻卡被廣泛應用在了門禁控制、金融支付、庫存管理等場景。在此背景下,各種安全認證機制應運而生,為保護個人隱私和敏感數據提供了可靠的保障,本文將通過一道 CTF 題目介紹 M1 卡采用的 AES認證機制,揭示其背后的原理。
    看雪論壇作者ID:NYSECbao
    本文示例是來自corCTF 2021中 的兩個內核題,由 BitsByWill 和 D3v17 所出。針對UAF漏洞,漏洞對象從kmalloc-64到kmalloc-4096,都能利用 msg_msg 結構實現任意寫。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类