Viết cho đỡ trống trải 🙂

Superchip (150)

Btc rất thích Virtual machine, hi vọng đây ko phải là 1 bài simulator 🙁

Như thường lệ,

~/D/mates  file superchip
superchip: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, with unknown capability 0x41000000 = 0x13676e75, with unknown capability 0x10000 = 0xb0401, not stripped

PowerPC… not fun. Lolz, freaking up now. Mình lười bà cố luôn, nhác đọc reference lắm …

Bật IDA check string coi thử biết đâu có flag… Không có 😀. Nhưng hàm main cũng không quá dài. Đại loại là

print "Please wait. I will show the flag for y..."

for i in range(26):
	r3 = maistr[i]  # offset maistr : 0x10010B14
	r4 = key[i % 8] # offset key : 0x10010B30 
	maistr = process(r3, r4)

print "The flag is %s" % maistr

Không có input gì hết, nên có thể chỉ cần chạy được là ra rồi. Be lazy, work smart!!!

  1. Download Ubuntu PowerPC:

http://cdimage.ubuntu.com/releases/12.04/release/ubuntu-12.04-desktop-powerpc.iso

2.

qemu-system-ppc -m 1024 -netdev user,id=network0 -device e1000,netdev=network0 -boot d -cdrom ubuntu-12.04-desktop-powerpc.iso

3.

wget https://blog.trich.im/files/superchip

4.

./superchip

/assets/wp-content/uploads/2016/03/mates4_re150_run.png

Nice xà! Đã chạy được, để đó đi làm bài khác vậy.

5) 12 hours later…

/assets/wp-content/uploads/2016/03/mates4_re150_run.png

LoL, really?!?

Phải đọc Assembly rồi 😅. Pseudo code của process như này

def process(a, b):
  c = 0
  for i in range(b):
    c = process2(c, a)
  return k

def process2(c, a):
  d = 0
  for i in range(c):
    d = process3(d)
  for i in range(a):
    d = process3(d)
  return d

def process3(d):
  i = 0
  while i != -1:
    i++
    d--
  return d

#Cac bien a,b,c,d,i la word (32bit)

OK vậy là do cái vòng lặp của process3 chạy 0xffffffff lần nên lâu như vậy mà vẫn chưa có output.

Nó sẽ ra kết quả khi i += 0xffffffff hay i -= 1 và d -= 0xffffffff hay đồng nghĩa với d = d + 1

Do đó ta optimize đoạn trên thành đổi i++ thành i--d-- thành d++ (vai trò như nhau). Nên hàm process sẽ tương đương

def process(a, b): return a * b

Từ đây solve bằng python khá dễ dàng

maistr = "DD A1 B4 45 75 6F 9C 8E  8B 0F CE 83 F3 5B CF B0 3F EE 0F A1 29 A4 4B D9  55 BD ".replace(' ','').decode('hex')
key = "D1 C1 B1 A1 47 4D 9B B5".replace(' ','').decode('hex')

ss = ""
for i in range(len(maistr)):
	ss += hex(ord(maistr[i]) * ord(key[i % 8]))[-2:]

print ss.decode('hex')

Tuy nhiên đang sẵn có qemu đang chạy nên mình patch lại cái bin luôn

printf '\x00\x01' | dd of=superchip bs=1 seek=1238 count=2 conv=notrunc 
printf '\xff\xff' | dd of=superchim bs=1 seek=1250 count=2 conv=notrunc

và …

/assets/wp-content/uploads/2016/03/mates4_re150_flag.png


Numbers (200)

Alright, mới thoạt nhìn thấy nhiều hàm tên dài thòn trông khá giống bài re250 của Whitehat Grandprix vòng loại 2015 😋.

Nhưng mà nhìn kỹ thì đó là tính toán trên các số thực chứ không phải là số lớn. Không có kỹ thuật gì gê gớm chủ yếu vẫn là đọc hiểu thôi và vận dụng các phép toán thôi. Nên để đọc nhanh mình có các lưu ý sau:

/assets/wp-content/uploads/2016/03/mates4_re250_show1.png

  • Search đọc nội dung từng tên hàm trước (đa phần là các mã asm convert thanh ghi số thực / gán nên không quan trọng)
  • Hide cast trong hexray để dễ đọc hơn
  • Debug tới những chỗ gán xong view vào check lại giá trị hex -> float.

/assets/wp-content/uploads/2016/03/mates_re250_show2.png

OK. Đầu tiên input 6 số thực

0 < t1, t2, t3, t4, t5, t6 < 200

6 cái if tiếp theo tương ứng 6 biểu thức sau:

(t5 + t6) ** 2 + t4 ** 2 == 14161
(t5 + t6) ** 2 + t3 ** 2 == 4900
t5 * t5 + 900 == t1 * t1
t6 * t6 + 900 == t2 * t2
t1 * t4 == 3570
t2 * t3 == 2100

Sau khi vận dụng hết khả năng tính nhẩm, tính ra được 6 số lần lượt là 34 50 42 105 16 40

Nếu mà lười quá thì vô sage hay z3 giải cũng được

> f1, f2, f3, f4, f5, f6, = var('f1, f2, f3, f4, f5, f6')
> solve( ((
  f5+f6)**2 + f4**2 == 14161 and 
  (f5+f6)**2 + f3**2 == 4900 and
  f5 * f5 + 900 == f1 * f1 and
  f6 * f6 + 900 == f2 * f2 and
  f1 * f4 == 3570 and
  f2 * f3 == 2100 and
  f1 > 0 and f1 < 200 and 
  f2 > 0 and f2 < 200 and
  f3 > 0 and f3 < 200 and
  f4 > 0 and f4 < 200 and
  f5 > 0 and f5 < 200 and
  f6 > 0 and f6 < 200) , f1, f2, f3, f4, f5, f6 )

Chưa test nữa, chắc được 😀

Tương tự vậy solve tiếp t1 t2 t3 t4 t5 t6 == 144 24 1 18 28 29

/assets/wp-content/uploads/2016/03/mates_re250_show3.png


Gadgets (350)

Cái tên gadget làm mình liên tưởng đến kĩ thuật ROP trong mảng exploit, kiếm từng gadget để gom thành 1 đoạn asm vi diệu… Nhưng đây là RE nên, có lẽ nào kiếm từng opcode để chạy chương trình. Oh no, Virtual machine ?!? 🙁

Vẫn như thường lệ, PeID / Resource hacker Check và Input thử. Không có gì đặc biệt ngoại trừ nhập đúng 9 kí tự thì tự động thoát chương trình. Có lẽ mỗi lần keypress sẽ có 1 hàm handle event này.

/assets/wp-content/uploads/2016/03/mates4_re350_show1.png

IDA View, search string và jump back chỗ gọi “Key format is …”. Đặt breakpoint cái đã.

/assets/wp-content/uploads/2016/03/mates4_re350_show2.png

Ngay lập tức ta thấy một dòng code khá đặc biệt, thường những đoạn như thế này sẽ là 1 địa chỉ của một đoạn mem được map trước đó. Đặt breakpoint.

Search string và jump back “The flag is …”, nhìn chung hàm này có một biến boolean để in flag và không check gì nữa. Ta lại đặt breakpoint.

/assets/wp-content/uploads/2016/03/mates4_re350_show3.png

Bắt đầu debug đến đoạn call edi như trên, ta thấy nó nhảy qua vùng địa chỉ mới như dự đoán.

Ở đây stack pointer (esp) sẽ được change sang vùng mem đó luôn, và ta thấy có rất nhiều địa chỉ function…

/assets/wp-content/uploads/2016/03/mates4_re350_show4.png

Tiếp tục debug cho đến khi chương trình dừng lại để input. Nhập 1 key và lại tiếp tục return về nhiều function trên đoạn stack ấy. Kinh nghiệm chinh chiến matesctf cho mình biết, đây đích thị là … Virtual machine 🙁

Trở lại đoạn stack trên ở 0x00250414 và bấm D liên tục để convert sang dạng dword 32bit, mình thấy có tới… 372 function, tuy nhiên bấm phải vào chọn Create Function, Rename chúng ta sẽ chỉ có tầm khoảng 10 opcode thôi (như trên)

Dịch và code lại pseudocode (script python):

from struct import unpack
ar = []
f = open('opcode.bin', 'rb').read() #extract tren stack tu 0x00250414
for i in range(371):
    ar.append(unpack("<I", f[4 * i:4 * i + 4])[0])

for op in ar:
    if op == 0x401320: #read index
        print "\nread(st[r1++])"
    elif op == 0x4015D0: #dec_index
        print "r1--"
    elif op == 0x4015C0: #inc_index
        print "r1++"
    elif op == 0x401450: #wtf1
        print '''r1 ^= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD'''
        
    elif op == 0x4015A0: #wtf2
        print "r2++"
    elif op == 0x401520: #gan1
        print "st[r2] = r1"
    elif op == 0x401550: #gan2
        print "r2 = r1"
    elif op == 0x401430: #cmp1
        print '''r1 ^= r2
if r1 == 0: r3 |= 2 else: r3 &= 0xFD'''
        
    elif op == 0x4013d0: #cmp2
        print '''r1 += st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD'''

    elif op == 0x401410: #cmp4
        print '''r1 -= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD'''

    elif op == 0x401580: #cmp5
        print 'swap(r1, st[r2])'

    elif op == 0x401470: #cmp6
        print '''r1 = ~r1
if r1 == 0: r3 |= 2 else: r3 &= 0xFD'''
    
    elif op == 0x401540: #gan3
        print "r1 = r2"

    else:
        print "NOP", hex(op)

Virtual machine code

#python
st[18] = 0
r1 = r2 = r3 = 0

#index 0
read(st[r1++])
r1--
r1 += 6
r1 ^= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2 += 9
st[r2] = r1
r1 = 1, r2 = 1

#index 1
read(st[r1++])
r1--
r1+= 26
r1 += st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2 += 9
st[r2] = r1
r1 = 2, r2 = 2

#index 2
read(st[r1++])
r1--
r1+= 26
r1 -= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
st[r2] = r1
r1 = 3, r2 = 3

#index 3
read(st[r1++])
r1--
r2 = r1
swap(r1, st[r2])
st[r2] = r1
r1+= 26
r1 -= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
swap(r1, st[r2])
r1 = r2 = 4

#index 4
read(st[r1++])
r1--
r2 = r1
swap(r1, st[r2])
st[r2] = r1
r1 = ~r1
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
st[r2] = r1
r1 = r2 = 5

#index 5
read(st[r1++])
r1--
r2 = r1
swap(r1, st[r2])
st[r2] = r1
r1 = ~r1
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
st[r2] = r1
r1 = r2 = 6

#index 6
read(st[r1++])
r1--
r1+= 8
r1 ^= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r1 = ~r1
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
st[r2] = r1
r1 = r2 = 7

#index 7
read(st[r1++])
r1--
r1+= 27
r1 += st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r1 = ~r1
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
st[r2] = r1
r1 = r2 = 8

#index 8
read(st[r1++])
r1--
r1+= 11
r1 -= st[r2]
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r1 = ~r1
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
r2+= 9
st[r2] = r1
r1 = r2 = 9

NOP 0x4015e0 => RC4(key=&st[9], keylen=9 , plaintext=0x004197F8)
r1 ^= r2
if r1 == 0: r3 |= 2 else: r3 &= 0xFD
NOP 0x401650 => in flag

Mô tả đoạn hoạt động của đoạn mã trên

  • Nhập 9 byte vào st[0 -> 8]
  • Biến đổi 9 byte (theo code trên) và lưu vào st[9 -> 17]
  • st[9 -> 12] == "\x6B\x7D\xB0\x1A" => continue
  • ciphertext = RC4(key=st[9 -> 17], keylen=9, plaintext=0x4197f8, len=64 byte)
  • ciphertext bắt đầu là “matesctf” thì in ra flag

Không phải ngẫu nhiên trước khi input, có hint là key (a-z){9}. Ta sẽ bruteforce 5 byte cuối của key theo input (vì 4 byte đầu là st[9->12] đã biết).

#include <stdio.h>
#include <stdlib.h>

unsigned char* KEY = "\x98\x0f\xdb\x3c\xe3\xea\x02\xaf";

void ksa(unsigned char state[], unsigned char key[])
{
   int i,j=0,t; 

   for (i=0; i < 256; ++i) state[i] = i; 
   for (i=0; i < 256; ++i) {
      j = (j + state[i] + key[i % 9]) % 256; 
      t = state[i]; 
      state[i] = state[j]; 
      state[j] = t; 
   }   
}

int prga(unsigned char state[])
{  
   int i=0,j=0,x,t; 
   unsigned char hi;

   for (x=0; x < 8; ++x)  {
      i = (i + 1) % 256; 
      j = (j + state[i]) % 256; 
      t = state[i]; 
      state[i] = state[j]; 
      state[j] = t; 
      if (state[(state[i] + state[j]) % 256] != KEY[x])
         return 0;
   }  
   return 1; 
}  

int main()
{

   unsigned char c1, c2, c3, c4, c5;
   unsigned char* key = strdup("\x6B\x7D\xB0\x1A\xCD\xCC\xC5\xA8\x22");
   unsigned char out[8];
   unsigned char state[256];

   unsigned int count = 0;
   for (c1 = 97; c1<=122; ++c1)
      for (c2 = 97; c2<=122; ++c2)
         for (c3 = 97; c3<=122; ++c3)
            for (c4 = 97; c4<=122; ++c4)
               for (c5 = 97; c5<=122; ++c5)
               {
                  count++;
                  if (count%100000==0) printf("%u\n", count);

                  key[4] = 255 - c1;
                  key[5] = 255 - c2;
                  key[6] = 255 - c3^14;
                  key[7] = 255 - (c4+34);
                  key[8] = 255 - (19 - c5);
                  ksa(state, key);
                  if (prga(state))
                     printf("---FUCKEN--- %c %c %c %c %c\n", c1, c2, c3, c4, c5);
               }
   return 0;
}

Final key = mblucabdh

flagmatesctf{Return 0riented Machine} 🙂