Skip to content

Feed to Win

September 28, 2019 / CTF

Writeups SwampCTF 2018

Giải này mình cảm thấy khá hay, phù hợp với mình (chắc tại trình mình cùi 🙁 ). Mình chỉ làm được tổng 4 bài RE và Pwn (đáng lẽ là 5)

Dragon's Horde (RE)

Bài đầu tiên trong RE và cũng là bài nhiều người làm được nhất. Mình không hiểu tại sao nhiều người làm được đến vậy trong khi mình còn không hiểu program flow của nó như thế nào 😐 Tại mới vào mình quăng nó vào IDA thì mấy vài hảm khả nghi

Xem thử mã giả của một trong số đó

1
2
3
4
int foo1(void)
{
  return std::__cxx11::basic_string<char,std::char_traits,std::allocator>::operator+=(&f, 102);
}

Để ý đến số 102 thì nó chính là kí tự f trong bảng mã ASCII nên mình đoán luôn mỗi hàm foo1 đến foo16 sẽ chứa một kí tự của flag.

1
2
3
a = [102, 108, 97, 103, 123, 114, 51, 118, 95, 49, 116, 95, 117, 112, 125]
print ''.join(map(chr,a))
# flag{r3v_1t_up}

Journey (RE)

Đầu tiên phải kiểm tra nó là file gì đã

1
2
3
$ file journey
 
journey: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped

Kéo vào IDA xem thử có gì hot ở trỏng

Chương trình gì mà có mỗi 2 hàm, đã vậy newbie như mình nhìn cái graph mà muốn khóc. Minh nghe các bro bảo RE hơn nhau ở sự kiên trì nên mình cứ debug F8 liên tục, rồi bất ngờ phát hiện ra điều thú vị ở thanh ghi EIP

Thấy ngay giá trị 0x0804... là hơi quen quen. Thường virtual address của mấy file ELF cũng bắt đầu bằng 0x0804... Nghi nghi chương trình này bị packed rồi mà lúc đó mình không nghĩ đến việc tìm tên packer mà lại lọ mọ debug tiếp mới vcl :'(. Ấn thử Ctrl+G xem nó có sinh ra những segment nào và nhảy đến để kiếm tra :))


Thấy cái signature là biết ngay mà nó bị packed rồi. Lúc đó mình hồn nhiên dump memory theo segments window của IDA mới vcl lần 2 :))

Sau khi dump thì mình dùng lệnh readelf -a dump.bin thì chỉ có được nhiêu đây thông tin

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
$ readelf -a dump.bin
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048736
  Start of program headers:          52 (bytes into file)
  Start of section headers:          724056 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         31
  Section header string table index: 28
readelf: Error: Reading 0x4d8 bytes extends past end of file for section headers
readelf: Error: Section headers are not available!
 
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0xa08c7 0xa08c7 R E 0x1000
  LOAD           0x0a0f5c 0x080e9f5c 0x080e9f5c 0x01024 0x01e28 RW  0x1000
  NOTE           0x0000f4 0x080480f4 0x080480f4 0x00044 0x00044 R   0x4
  TLS            0x0a0f5c 0x080e9f5c 0x080e9f5c 0x00010 0x00028 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x0a0f5c 0x080e9f5c 0x080e9f5c 0x000a4 0x000a4 R   0x1
 
There is no dynamic section in this file.
 
Displaying notes found at file offset 0x000000f4 with length 0x00000044:
  Owner                 Data size       Description
  GNU                  0x00000010       NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.32
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
 
    Build ID: a8fa0fd0707985f001f0ccb144fd599c3d8f6387

Mất mát một số thứ nhưng nó vẫn để lại Entry point address: 0x8048736 là ngon rồi. Mình nhảy đến entry point xem có gì (trong IDA ấn g để nhảy đến địa chỉ mong muốn, rồi ấn c để disassemble nó)

Để ý ngay lúc này có 4 cái địa chỉ là loc_80487F0, loc_8049750, loc_804887C và unk_8049020. Bằng yếu tố tâm linh mình xác định được ngay loc_804887C chính là hàm main (thực ra là mình click vào từng cái rồi kiểm tra string =)))

Nhảy đến địa chỉ 0x804887C rồi ấn c rồi ấn p rồi ấn F5 ta được mã giả hàm main

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
int sub_804887C()
{
  char v0; // ST18_1
  signed int v1; // edx
  int result; // eax
  int v3; // ecx
  unsigned int v4; // et1
  int i; // [esp+0h] [ebp-38h]
  int v6; // [esp+4h] [ebp-34h]
  int v7; // [esp+10h] [ebp-28h]
  signed int v8; // [esp+14h] [ebp-24h]
  char v9[18]; // [esp+1Ah] [ebp-1Eh]
  unsigned int v10; // [esp+2Ch] [ebp-Ch]
 
  v10 = __readgsdword(0x14u);
  ((void (__cdecl *)(const char *))unk_804F640)("As you play this game, there will be many adventures for you to take, quests on the side of this great journey.");
  ((void (__cdecl *)(const char *))unk_804F640)(
    "This is one of the quests here, and you may not know it yet. You will know when you complete the test, because all o"
    "f the die will have been cast in your favor.");
  ((void (__cdecl *)(const char *))unk_804F640)("Prove your worth, enter a password to continue!");
  ((void (*)(const char *, ...))unk_804F070)("%17s", v9);
  v6 = ((int (__cdecl *)(char *))unk_805B8C0)(v9);
  v7 = 1741837201;
  v8 = 3313467;
  for ( i = 0; i < v6; ++i )
  {
    v0 = ((int (__cdecl *)(int, signed int, signed int, _DWORD))unk_8048B60)(v7, v8, 10, 0);
    v7 = ((int (__cdecl *)(int, signed int, signed int, _DWORD))unk_80489F0)(v7, v8, 10, 0);
    v8 = v1;
    v9[i] -= v0;
  }
  if ( ((int (__cdecl *)(char *, const char *))unk_8048280)(v9, "theresanotherstep") )
  {
    ((void (__cdecl *)(const char *))unk_804F640)("Mission failed! You must try again, giving up was never an answer if you have gotten this far!");
  }
  else
  {
    ((void (__cdecl *)(const char *))unk_804F640)(
      "Congratulations, you have reached the next stage in the mission. As close as you are, you can almost smell the perfect role!");
    ((void (__cdecl *)(const char *))unk_804F640)(
      "However, no tricky business is allowed. It's about the journey, not the destination, so the password you entered does matter.");
    ((void (__cdecl *)(const char *))unk_804F640)("In fact, the password is the answer! Submit in the form flag{...}");
  }
  result = 0;
  v4 = __readgsdword(0x14u);
  v3 = v4 ^ v10;
  if ( v4 != v10 )
    result = ((int (__fastcall *)(int))unk_806F6F0)(v3);
  return result;
}

Hình như file này sử dụng static linking thì phải, mình thử ấn vào hàm in chuỗi unk_804F640 thì lại lòi ra một đống mã assembly khác. Khá là bối rối, không biết tên hàm thì làm sao mà biết hàm đó làm gì, giờ chỉ còn mỗi một cách là đoán thôi :))

Để ý dòng từ dòng 32, if (unk_8048280(v9, "theresanotherstep")) thì đây chính là điều kiện để mình có được flag. Thực hiện trace ngược từ dòng 32 trở lên thì v9 phụ thuộc vào v0 và v9 cũng chính là input, v0 = unk_8048B60(v7, v8, 10, 0) với v7 và v8 có giá trị ban đầu là hằng số. Vậy cách giải quyết khá đơn giản, lưu hết lần lượt các giá trị của v0 rồi sau đó lấy mã ASCII của từng kí tự trong chuỗi theresanotherstep cộng với các giá trị v0 tương ứng

Code:

1
2
3
4
5
6
7
8
9
text = 'theresanotherstep'
v0_list = [3, 3, 4, 2, 1, 2, 3, 4, 1, 4, 3, 2, 1, 3, 2, 4, 1]
flag = ''
 
for i in range(len(text)):
    flag += chr(ord(text[i]) + v0_list[i])
 
print(flag)
# wkitfudrpxkgsvviq

2 bài Pwn mình hơi lười viết (tại trình cùi) nên chỉ nói sơ sơ qua thôi.

Apprentice's Return (Pwn)

Chương trình cho mình sẵn mình lỗi buffer overflow ngay tại hàm doBattle để có thể control được return address của hàm đó. Tuy nhiên, khi overwrite return address thì phải thỏa điều kiện retaddr < 0x8048595. Để ý ngoài việc có thể control được return address thì mình có thể điều khiển luôn của cả saved EBP

Để ý dòng có gạch đỏ, từ việc điều được EBP thì mình có thể điều khiển được địa chỉ lưu buffer của hàm read. Vậy mình sẽ ghi đè giá trị của puts trong bảng GOT với giá trị là 0x8046812 để in ra flag.

Power QWORD (Pwn)

Bài này bắt phải dùng ropgadget rồi. Lúc bắt đầu làm bài này tự dưng mình lại muốn đi tắm vì nóng quá, tự dưng trong lúc tắm lại nhớ đến one_gadget Và thế là xong :)) giải bài này một cách nhanh gọn :))

 

Note:

Sau khi mình đọc lại writeup của các đội khác thì thấy câu journey là packer là upx =)) gõ lệnh 1 phát là unpack được, cay =))

Post navigation

Previous Post:

CrackMe – VolgaCTF 2018 Quals

Next Post:

Writeups BlazeCTF 2018

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