MEEPWN CTF 2018 - XSS

Lúc đầu nhìn tên bài mình cứ nghĩ là dạng binary .Net chứa mấy đề XSS giống ở SECCON, nên còn không buồn đọc đề. Đến lúc làm thì thấy là cũng liên quan thật.

File binary khi được chạy sẽ fork thêm 1 process nữa, rồi process mới này lại fork một process khác, tổng là 3 process. Mỗi process này sẽ dựa theo số lượng tham số truyền vào để xử lý theo nhánh riêng của nó (process đầu không có tham số, process thứ hai có 1 tham số là 1, process thứ ba có 2 tham số là 1 và 2). Mình debug bằng cách attach 3 cửa sổ IDA vào lần lượt 3 process.

Nếu cứ diễn giải lần lượt từng bước thì khá dài dòng, về cơ bản là các process sẽ tương tác với nhau bằng cách inject code và thực thi thông qua các hàm: OpenProcess, VirtualAllocExReadProcessMemoryWriteProcessMemory, WaitForSingleObject, ReleaseSemaphore,  và CreateRemoteThread. Các code được inject tương đối giống nhau, có dạng thế này (đây là code đọc input từ người chơi):

    v11 = 0;
    hObject = 0;
    while ( WaitForSingleObject(*(&dword_4F4FFC + (_DWORD)lpParameter), 1u) == 258 )
        ;
    arr1[0] = 97;
    arr1[1] = 98;
    arr1[2] = 99;
    arr1[3] = 100;
    arr1[4] = 97;
    arr1[5] = 98;
    arr1[6] = 99;
    arr1[7] = 100;
    arr1[8] = 97;
    arr1[9] = 98;
    arr1[10] = 99;
    arr1[11] = 100;
    arr1[12] = 97;
    arr1[13] = 98;
    arr1[14] = 99;
    arr1[15] = 100;
    arr2[0] = 106;
    arr2[1] = 21;
    arr2[2] = 109;
    arr2[3] = 11;
    arr2[4] = -99;
    arr2[5] = -16;
    arr2[6] = -62;
    arr2[7] = 52;
    arr2[8] = 116;
    arr2[9] = -118;
    arr2[10] = -44;
    arr2[11] = 79;
    arr2[12] = 80;
    arr2[13] = -124;
    arr2[14] = -96;
    arr2[15] = 127;
    for ( i = 0; i < 16; ++i )
    {
        v8 = getchar();
        arr1[i] = v8 ^ arr2[i];
    }
    while ( WaitForSingleObject(*(&hHandle + (_DWORD)lpParameter), 1u) == 258 )
        ;
    hProcess = OpenProcess(dwDesiredAccess, 0, *(&BaseAddress + (_DWORD)lpParameter));
    if ( !hProcess )
        exit(0);
    v13 = 0;
    lpBaseAddress = (LPCVOID)BaseAddress;
    Buffer = 0;
    while ( 1 )
    {
        v13 = ReadProcessMemory(hProcess, lpBaseAddress, &Buffer, 1u, 0);
        if ( !v13 )
            exit(0);
        if ( Buffer == 198 )
            break;
        lpBaseAddress = (char *)lpBaseAddress + 1;
    }
    lpBaseAddress = (char *)lpBaseAddress - 1;
    for ( j = 0; j < 16; ++j )
    {
        v13 = WriteProcessMemory(hProcess, (char *)lpBaseAddress + 4, &arr1[j], 1u, 0);
        if ( !v13 )
            exit(0);
        lpBaseAddress = (char *)lpBaseAddress + 4;
    }
    CloseHandle(hProcess);
    hObject = OpenProcess(dwDesiredAccess, 0, dword_4F502C[2]);
    if ( !hObject )
        exit(0);
    dwSize = 43632;
    v10 = VirtualAllocEx(hObject, 0, 0xAA70u, 0x1000u, 0x40u);
    if ( !v10 )
        exit(0);
    v6 = v10;
    lpBuffer = byte_4FFAA8;
    v11 = WriteProcessMemory(hObject, &BaseAddress, &v10, 4u, 0);
    if ( !v11 )
        exit(0);
    for ( k = 0; k < dwSize; ++k )
        byte_4FFAA8[k] ^= 0x66u;
    v11 = WriteProcessMemory(hObject, v6, lpBuffer, dwSize, 0);
    if ( !v11 )
        exit(0);
    lpStartAddress = (LPTHREAD_START_ROUTINE)v10;
    v4 = CreateRemoteThread(hObject, 0, 0, (LPTHREAD_START_ROUTINE)v10, lpParameter, 0, 0);
    if ( !v4 )
        exit(0);
    CloseHandle(hObject);
    return ReleaseSemaphore(*(&hSemaphore + (_DWORD)lpParameter), 1, 0);
}

Mình sẽ không nói chi tiết về việc binary dùng SemaphoreCreateRemoteThread như thế nào để điều khiển luồng thực thi cho đúng, vì nói thật nó loằng ngoằng và mình cũng không cố note lại. Tuy nhiên, để làm được bài thì vẫn cần biết chương trình làm những gì, nên sau khi F8 tỉ mỉ một vài lượt inject code đầu tiên, nhận thấy có sự tương đồng như đã nói khi nãy, mình đẩy nhanh tiến độ bằng cách đặt breakpoint ở hàm kernel32_CreateRemoteThread(trong cả 3 process) và cứ thế chạy, mỗi khi dừng ở breakpoint, mình dựa vào tham số lpStartAddress để biết địa chỉ phần code chuẩn bị được thực thi ở đâu, rồi mở xem code đó nó làm gì (đáng lẽ cần phải xem tham số hProcess để biết là process nào nữa, nhưng mình lười nên cứ Alt+Tab lần lượt 3 cửa sổ IDA để thử thôi). Cũng có một chút anti-decompile nhưng có thể fix bằng tay được. Nhìn chung chương trình hoạt động như thế này:

Các đoạn code giống nhau về cấu trúc, có sẵn hai mảng 16 ký tự là arr1 và arr2, trong đó mảng arr1 luôn là abcdabcdabcdabcd. Khi được thực thi bằng hàm CreateRemoteThread, code sẽ xử lý 2 mảng arr1 và arr2 (bằng một trong các thao tác xor, aesencaesenclast hoặc so sánh), sau đó, ghi đè output  vào mảng arr1 của đoạn code thực thi sau đó thông qua hàm `WriteProcessMemory` (đoạn code này vốn cũng đã thực thi rồi, nhưng đang bị dừng bởi Semaphore). Bên cạnh việc xử lý input thì mỗi thread cũng kiêm thêm việc ghi code cho các thread sau. Để dễ hình dung (vì nếu nói chính xác thì các đoạn code không chạy một mạch từ đầu đến cuối mà sẽ bị gián đoạn bởi Semaphore), có thể tưởng tượng với đầu vào là input mà ta nhập, chương trình cho nó đi qua một dây chuyền sản xuất, ở mỗi bước giá trị của input lại bị thay đổi một tí, xong được đẩy sang bước tiếp theo. Có tất cả là 1 bước xor, 9 bước aesenc, 1 bước aesenclast (AES128) và cuối cùng là 1 bước so sánh (để hiện thông báo Correct! hoặc Try Again!). Chúng ta có toàn bộ hardcoded key được dùng trong các bước này và tất nhiên là cả giá trị so sánh cuối cùng.

xor_key = '6A156D0B9DF0C234748AD44F5084A07F'

aes_keys = [
'609D4731B5DD367EEF997AD8495C4523',
'FAECDBBB93B23AEF68E4BE6D2FF66B4C',
'3E43953C69D073672297D1B1A361FD4A',
'45337E3CF07F2DAC33443B75482AC546',
'4DF0EC1A3D04A9DBF5D5081A80709306',
'BAF0AB1FAC2F5881F125B159F979DE03',
'34AFFF57513A0FEC8BA0E65F8C986078',
'2EEBCF4605AE3D94BA8CCCF44CA11D4C',
'74F9C5427F7A6EE2B11F2CC21804B8F7',
'0A98631D8469820807CA31F71D335629',
]

target = '68CEDFDD586C37E4C4E1ACB4097F97A4'

Đến đây thì phần RE đã xong, phần còn lại là Crypto, mình hỏi một chuyên gia mật mã đầu ngành của team và nhận lại flag vài phút sau đó.

P.S: Có một bí ẩn xung quanh challenge này, nhưng bây giờ cảm giác tội lỗi với Linh Ka khiến mình không tập trung làm được việc gì cả. Nếu sau này có thời gian tìm hiểu thêm thì mình sẽ bổ sung vào bài viết sau.

Quên mất, cá nhân mình đánh giá đây là một đề mất không ít thời gian để xây dựng, nên kudos to người ra đề!

MEEPWN CTF 2018 - PyCalx2

Bài này code giống hệt bài 1, khác mỗi cái là biến op bây giờ cũng không được chứa các ký tự trong mảng ['(',')','[',']','\'','"'] nữa, do đó không thể dùng kiểu +' để break giá trị của value2 ra ngoài chuỗi. Nói cách khác, value1 và value2 đều sẽ chỉ là các string đơn thuần.

Mình search thử thì thấy Python3.6 có một tính năng mới là Formatted string literals, hoạt động như sau:

Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 17:54:52) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 'xxx'
>>> f'{a}'
'xxx'
>>> f'{0 if 2 > 3 else 1}'
'1'

Có thể thấy đây gần như là hướng duy nhất của bài này.

Nếu op = +f, chuỗi được eval sẽ có dạng  calc_eval = 'value1'+f'value2'. Tương tự bài 1, chúng ta muốn dựa vào output của hàm eval để leak flag, mà output chỉ được in ra khi kết quả eval là số, là 'True' hoặc 'False'. Như thế dẫn đến một số suy luận sau:

  1. value1 kiểu gì cũng chỉ là chuỗi đơn thuần, nên sau khi eval nó sẽ vẫn là chính nó. Mặt khác, value1 không thể là số vì nếu thế, value2 cũng phải là số, chẳng có tác dụng gì. Vậy, value1 là chữ.
  2. Nếu value1 là chữ, thì kết quả sau khi eval không thể là số, nghĩa là chúng ta chỉ có thể thấy được output nếu eval ra True hoặc False, thì value1 cần là một phần của True hoặc False (ví dụ Tr hoặc Fals, để sau khi kết hợp với value2 sẽ ra giá trị hoàn chỉnh). Về lý thuyết chúng ta có thể để value2 trả về đầy đủ chuỗi True hoặc False, tuy nhiên đề bài không cho phép value1 là một chuỗi rỗng.
  3. Dạng biểu thức f'{0 if 2 > 3 else 1}' có thể cho phép chúng ta leak flag giống bài 1, kiểu như f'{'e' if 'MeePwn' in FLAG else 'x'}'. Với biểu thức này, kết hợp với value1 = Tru thì khi nhận được output là True, chúng ta kết luận được FLAG có chứa chuỗi MeePwn, và ngược lại.

Tất nhiên ở trên chỉ là ví dụ thôi, vì thực tế đề bài không cho dùng ' trong value2. Sau một hồi ngồi nhìn màn hình, mình nghĩ ra một hướng khá là cần cù bù thông minh.

Content-Disposition: form-data; name="value1"

Tru
----------954427002
Content-Disposition: form-data; name="op"

+f
----------954427002
Content-Disposition: form-data; name="value2"

{source % 101 if source % 77 in FLAG else x}
----------954427002
Content-Disposition: form-data; name="source"

%c

Output nhận được là True (hợp lý vì FLAG có chứa chữ M trong từ MeePwn).

Nhưng check từng ký tự thì chỉ có thể tìm được charset của FLAG mà thôi, mình cần check được nhiều hơn. Do value2 dài tối đa 64 ký tự, mình cố gắng xóa bớt mấy khoảng trắng, được như sau:

Content-Disposition: form-data; name="value1"

Tru
----------954427002
Content-Disposition: form-data; name="op"

+f
----------954427002
Content-Disposition: form-data; name="value2"

{source%101 if source%77+source%101+source%101 in FLAG else x}
----------954427002
Content-Disposition: form-data; name="source"

%c

3 ký tự một lúc là khá ổn, đủ để mình tìm được đến MeePwnCTF{python3.6[_strikes_backk. Sau đó thì phát sinh một vấn đề, đó là với 2 ký tự kk, mình tìm được 2 chuỗi thỏa mãn là kkk và kk}. Cái sau chắc là phần kết của FLAG rồi, nhưng cái đầu thì khá là bế tắc, do không biết cần chính xác bao nhiêu chữ k. Mình có thể thử submit dần luôn trên scoreboard, nhưng ngại nhỡ bị ban thì mang tội với team, còn nếu clone một nick mới để thử thì lại làm tăng số đội giải được, điểm của mình cũng bị ảnh hưởng. Đều không vẹn toàn.

Tự nhiên mình nghĩ đến một hướng, mặc dù đoán là không được nhưng vẫn quyết định thử:

>>> '%c' % 49 * 3
'111'

Kỳ diệu. Mình cứ sợ là nó sẽ thành '%c' % 147.

FLAG: MeePwnCTF{python3.6[_strikes_backkkkkkkkkkkk)}

P.S: Bài này Jinmo làm theo một cách thực sự rất đẹp và đáng được ngợi ca, mọi người có thể đọc tại đây. Kudos to him!

 

What is SafeFinder/OperatorMac campaign?

 

A new variant of adware was just discovered yesterday. It’s going viral on Twitter and other media, since they use valid Apple developer certificate to sign all packed samples. I’m quite overbusy these days but it got my interest when seeing the name stated in that certificate: “Quoc Thinh”, quite a unique Vietnamese name. So why not take a break from desperate thesis, toss adware in my lame automated MacOS analysis framework and see what our ‘countryman’ doing?

Continue reading What is SafeFinder/OperatorMac campaign?

ASISCTF 2017 - Web - Secure portal [1,2,3]

Hey yo!

Long time no C++
Có chút vấn đề khi mình viết writeup cho bài này đó là phần lầy flag có vẻ không hoạt động 🙁 nên hiện tại mình chỉ có thể viết được phần 1.

Okie, link

Có gợi ý như sau By the way I know it's dangerous to code with IDE in server-side but I really obey security instructions.

Vào trang web và lưu lại các request & response bằng Burpsuite Proxy

Continue reading ASISCTF 2017 - Web - Secure portal [1,2,3]

Whitehat Contest 12 - Pwn400

Líp vẫn nhớ và yêu Híp nhiều lắm ...

Chiến đấu bên những người anh em luôn làm tôi cảm thấy thoải mái và phấn khích. Đợt contest này cả đội đã có 1 ngày không ngủ, cũng gần như không ăn, chỉ dùng lon bò húc để cầm hơi. Cả đội đã rất nỗ lực và hy vọng tràn trề khi lúc đầu liên tục tranh giành top 1 với kỳ phùng địch thủ. Cho đến giữa đêm, do thiếu một chút may mắn ( tôi thì không nghĩ vậy, đêm là khoảng thời gian tôi hay rơi vào trạng thái không ổn định pudency ) mà mọi người mất dần ý chí chiến đấu, nhìn đội khác vươn lên. Có lẽ nếu như tôi dành được 400 điểm từ bài này thì mọi chuyện đã khác. Nhưng dù sao "có lẽ" vẫn chỉ là 1 từ người ta dùng để biện hộ cho lỗi lầm của mình mà thôi.
Continue reading Whitehat Contest 12 - Pwn400

An toàn tính toán đa thành viên

Multi-Party Computation (MPC) là một khái niệm được các nhà mật mã học đắn đo nghiên cứu tận những thập niên 80 thế kỷ trước. Xuất phát tự nhiên từ những bài toán học búa trong cuộc sống phải đặt ra một giao thức hay ho hơn để đánh đố nhau. Ví dụ năm 1982 đó là bài toán triệu phú của anh Yao (1982 Andrew Yao  1), diễn Nôm đơn giản là anh Bin có số A tiền, còn anh Job có số B tiền. Hai anh trong một cuộc nhậu lỡ thách nhau xem ai có nhiều tiền hơn ai surrender. Nhưng hai anh đều không muốn lộ ra tổng số tiền mình có cho nhau biết. Do đó mới nảy sinh bài toàn chứng minh bất đẳng thức A ≥ B mà không lộ thông tin nào của A và B cho bất cứ ai, kể cả 2 anh Bin và anh Jobs. Giải quyết xong bài toàn này đã mở ra một kỷ nguyên mới cho bảo mật thông tin đặc biệt là thương mai điện tử, data mining khi muốn so sánh các giá trị, tính toán cộng trừ nhân chia mà vẫn bảo vệ được thông tin mật như số tiền, tổng tiền trong tài khoản khách hàng, thông tin nhân khẩu học v.v.

Continue reading An toàn tính toán đa thành viên

Malware Analysis Overview for beginners

 

 

The malware threat landscape is continuously evolving. In this blog post, I would like to introduce the basic concept of malware and malware analysis, the ideas of both static and dynamic malware analysis. Besides, malware evasive techniques and novel solutions will be introduced as well as modern research such as automatic protocol RE and Android malware behavior analysis will be mentioned in last sections.

Continue reading Malware Analysis Overview for beginners