Home (CTF) LINE CTF 2023 fishing writeup
Post
Cancel

(CTF) LINE CTF 2023 fishing writeup

Date: March 25, 2023 rank: 60 team: ST4RT

fishing - rev ( 166 pt, 32 solve )

https://i.imgur.com/fUf4zJ1.png

https://i.imgur.com/VaB8tlK.png

In _main function, calls _do_global_ctors and functions at 0x140004100 are called in turn

_main 함수에서 _do_global_ctors 함수를 호출 한 뒤, 0x140004100 에 있는 함수들이 차례로 호출된다.

https://i.imgur.com/2ICL1ql.png

those function can’t be decompiled due to anti disassemble jumping to opcode+1 . so I wrote idapython script to fix that anti disassemble

해당 함수들을 디컴파일해보면,opcode+1 로 점프해 디스어셈블러가 망가지는 것을 확인할 수 있다. idapython 으로 저렇게 생긴 부분들 정상적으로 디컴할 수 있게하는 스크립트 작성

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
import re
import time
functions = [(0x140001AE6,0x140001B12),(0x140001B13,0x140001ba3),(0x140001BA4,0x140001cde)]
functions = [(0x1400020FA,0x14000222F)]
functions = [(0x1400017d0,0x140001ae5)]
pattern = r"jmp.+(14[\\da-fA-F]{7}\\+1)"
for f in functions:
    start,end=f
    print('start',hex(start))
    while start < end:
        create_insn(start)
        line = generate_disasm_line(start,0)
        print(line)
        if re.search(pattern,line):
            print(line)
            patch_byte(start,0x90)
            create_insn(start)
            start+=2
        if('db' in line):
            patch_byte(start,0x90)
            create_insn(start)
            start+=1
        else:
            start+=1
    print('end',hex(end))

print(time.time())
print('+'*30)

0x140001AE6 -> call AddVectoredExceptionHandler, handler → 0x1400017D0

0x140001B13 -> get gs register to detect debugger

0x140001BA4 -> get gs register, GetProcessHeap to detect debugger

checking Xref to _main, sub_1400020FA calls _main

after calling the _main function, it gets input and creates a thread with input as an argument and call SetThreadContext to set Dr0~3 register

sub_1400020FA 함수에서 _main 함수를 호출한 뒤, 문자열을 입력받고 해당 문자열을 인자로 스레드를 생성하는 루틴을 볼 수 있었다. 스레드를 생성하고, ThreadContext 설정

1
2
3
4
Context.Dr0 -> xor 0x21 func
Context.Dr1 -> sub 0x22 func
Context.Dr2 -> xor 0x11 func
Context.Dr3 -> add 0x12 func

Thread Handler

  1. call xor 0x21 with input
  2. call sub 0x22 with input
  3. call xor 0x11 with key
  4. call add 0x12 with key
  5. custom RC4 encryption
  6. compare

Since there is only exit after detecting debugger, I can get RC4 key, patching exit to ret.

디버거를 단순 탐지 후 exit 하는 것 밖에 없기 때문에, exit 패치하고 4 까지 가면, 사용되는 키 값을 알 수 있다.

key : m4g1KaRp_ON_7H3_Hook

https://ling.re/hardware-breakpoints/

But when I xor, sub, RC4 my input, it doesn’t match to values in debugger

그런데, thread handler 루틴 그대로 해도 input 을 암호화 한 결과가 디버깅할 때 값이랑 일치하지 않는다.

it’s due to AddVectoredExceptionHandler, when the functions in Dr0~3 are called, the handler is executed first, then the function is executed

이것은 위에서 AddVectoredExceptionHandler 으로 Debug Register 핸들러를 등록하고, Dr 레지스터를 설정해서 다른 루틴이 추가로 실행되기 때문이다.

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
__int64 __fastcall sub_1400017D0(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
...
  if ( ExceptionInfo->ExceptionRecord->ExceptionCode != 0x80000004 )
    return 0i64;
  if ( (ExceptionInfo->ContextRecord->Dr6 & 1) != 0 )
  {
    Rcx = ExceptionInfo->ContextRecord->Rcx;
    Rdx = ExceptionInfo->ContextRecord->Rdx;
    for ( i = 0; i < Rdx; ++i )
      *(i + Rcx) ^= i;
  }
  else if ( (ExceptionInfo->ContextRecord->Dr6 & 2) != 0 )
  {
    v5 = ExceptionInfo->ContextRecord->Rcx;
    v4 = ExceptionInfo->ContextRecord->Rdx;
    for ( j = 0; j < v4; ++j )
      *(j + v5) = (*(j + v5) >> 5) | (8 * *(j + v5));
  }
  else if ( (ExceptionInfo->ContextRecord->Dr6 & 4) != 0 )
  {
    v12 = 105;
    v7 = ExceptionInfo->ContextRecord->Rcx;
    v6 = ExceptionInfo->ContextRecord->Rdx;
    for ( k = 0; k < v6; ++k )
    {
      *(k + v7) ^= v12;
      v12 *= v12;
    }
  }
  else if ( (ExceptionInfo->ContextRecord->Dr6 & 8) != 0 )
  {
    v9 = ExceptionInfo->ContextRecord->Rcx;
    v8 = ExceptionInfo->ContextRecord->Rdx;
    for ( m = 0; m < v8; ++m )
    {
      *(m + v9) = 2 * *(m + v9) + 3 * m;
      *(m + v9) ^= m + 5;
    }
  }
  ExceptionInfo->ContextRecord->Dr6 = 0i64;
  ExceptionInfo->ContextRecord->EFlags |= 0x10000u;
  return 0xFFFFFFFFi64;
}

Which BP is affected can be known according to each bit of Dr6.

1
2
3
4
Context.Dr0 -> xor 0x21 -> xor idx
Context.Dr1 -> sub 0x22 -> ror 5
Context.Dr2 -> xor 0x11 -> v12=105, xor V with v12, v12 ** 2
Context.Dr3 -> add 0x12 -> ( V * 2 + 3 * idx ) ^ ( idx + 5 )

When I debug, some handlers didn’t work. So the input encryption routine is finally :

디버깅 해보니까, input 암호화 할 때 작동하지 않는 핸들러도 있었다. 그래서 input 암호화 루틴은 최종적으로 다음과 같다.

  1. xor with 0x21
  2. sub 0x22 handler
  3. sub with 0x22
  4. custom RC4 with key “m4g1KaRp_ON_7H3_Hook”

solve.py

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
key=b"m4g1KaRp_ON_7H3_Hook"
flag=list(map(lambda x:int(x,16),"D0 BE 9F 5A BD F0 34 B5 D0 6F FB E2 99 BA AE D7 36 D5 2D C2 22 45 B0 03 9D 63 66 53 C7 28 CC 2A 2B 14 BB 09 9B E3 60 46 3A 00 00 00 00 00 00 00".split()))

## RC4
def PSK(t,key):
    for i in range(256):
        t[i] = i
    v6=0
    for j in range(256):
        v6 = (t[j]+v6+key[j%len(key)])&0xff
        v4 = t[j]
        t[j]=t[v6]
        t[v6]=v4

def enc(v7,data,out):
    v12 = 0
    v11 = 0
    for i in range(0x28):
        v12 = v12+1
        v11 = (v7[v12]+v11)&0xff
        v9 = v7[v12]
        v7[v12]=v7[v11]
        v7[v11]=v9
        v8 = v7[(v7[v12]+v7[v11])&0xff]
        out[i] = ((v11-24)^v8^data[i])&0xff

def xor_21(buf):
    for i in range(len(buf)):
        buf[i] ^= i
def sub_22(buf):
    for i in range(len(buf)):
        ## ror 5
        # buf[i] = ((buf[i]<<3)&0xff) | ((buf[i]>>5)&0x07)
        ## rol 5
        buf[i] = ((buf[i]<<5)&0xff) | ((buf[i]>>3)&0x1f)
def xor_11(buf):
    v12 = 105
    for i in range(len(buf)):
        buf[i] ^= v12
        v12 = v12**2 & 0xff
def add_12(buf):
    for i in range(len(buf)):
        buf[i] = 2 * buf[i] + 3 * i
        buf[i] &=0xff
        buf[i] ^= i+5

table=[0]*256
PSK(table,key)
out=[0]*0x28
enc(table,flag,out)
out = list(map(lambda x:x+0x22&0xff,out))
sub_22(out)
out = list(map(lambda x:x^0x21,out))

print(bytes(out))
## LINECTF{e255cda25f1a8a634b31458d2ec405b6}

This post is licensed under CC BY 4.0 by the author.
Trending Tags