linux process 생성

linux에서는 fork(), vfork(), clone() 시스템 콜로 자식 프로세스를 생성 할 수 있다.


fork, vfork, clone 모두 do_fork()를 호출한다.
fork, vfork, clone 모두 인터럽트를 이용해서 커널의 sys_fork, sys_clone, sys_vfork를 호출하고 이 세 함수 모두 특정 플래그를 첫번째 인자로 넘겨 결국엔 do_fork()를 호출한다.

세번 째 인자인 pt_regs는 커널스택의 레지스터값이고 스택에도 pt_regs구조체에 맞게 레지스터 값이 들어간다.(pt_regs는 윈도우즈 context구조체같이 레지스터값들을 변수로 갖고있다)

do_fork()함수는 부모 프로세스로부터 clone_flags값에 따라 여러 자원들을 복사하는 일을하고 실제 처리는 copy_process()에서 일어난다.

long do_fork(unsigned long clone_flags,
                    unsigned long stack_start,
                    struct pt_regs *regs,
                    unsigned long stack_size,
                    int __user *parent_tidptr,
                    int __user *child_tidptr)

fork, vfork, clone 모두 부모 프로세스와 동일한 프로세스를 자식 프로세스로 생성하였다.
execve 는 사용하던 공간을 해제하고 새로운 공간에 다른 프로세스를 로드하고 실행한다.

커널안에 sye_execve()로 정의되어 있는 execve() 는 do_execve()를 호출한다. do_fork()처럼 커널 스택의 pt_regs의 주소를 인자로 넘겨준다. do_execve()도 여러 자원을 복사해두고 바이너리 로더를 하나하나 테스트한다.
* exe*()함수 전부 execve시스템콜 호출

elf파일은 elf 바이너리 로더를 사용하고 elf_load_binary()함수를 통해서 바이너리를 로딩한다.

elf_load_binary()함수의 개요
1. 헤더 체크
2. 프로그램 헤더를 읽는다.
3. 파일 디스크립터 구조체의 공유를 중지 - unshare_files()
4. ELF 헤더에서 인터프리터가 지정되어 있을 경우 대상파일을 오픈하고 헤더체크
5. 새 프로그램을 로드하기 위한 준비 - flush_old_exec()
6. 실행 파일등을 스택에 매핑 - setup_arg_pages()
7. 헤더에 따라 elf_map함수를 호출해 프로그램 매핑
8. 인터프리터 지정시 매핑 - load_elf_interp()
9. 프로그램 실행할 때 레지스터(pt_regs)초기화.


리눅스의 bash도 위의 fork와 execve를 사용하여 명령어를 실행한다.

기본적으로 bash는 무한루프를 돌면서 사용자의 입력을 기다리고 있는다.
사용자가 명령어를 입력하면 fork()를 이용하여 bash 자신과 똑같은 자식프로세스를 하나 생성한다.
fork()의 return 값에 따라 부모는 자식 프로세스가 끝날 때까지 기다리는 루틴으로 진입하고 자식프로세스는 execve(사용자의입력)를 호출하게된다.
execve()가 호출되면서 bash 자식프로세스는 메모리에서 내려가고 execve()함수에 의해서 사용자의입력 프로그램이 새롭게 메모리에 올라가게된다.
bash 자식 프로세스가 죽었으니 bash 부모 프로세스는 다시 사용자의 입력을 기다리게된다.
이런식으로 명령어를 실행하게 된다.


참고문헌 : 리눅스커널2.6구조와원리
Read more

_start, __libc_start_main, main

_start, __libc_start_main, main

hello를 출력하는 hello



C언어로 작성한 간단한 elf파일의 헤더를 보면 실행파일의 시작주소값인 Entry point address에 _start 의 주소가 들어있는가 있다. 프로그램을 실행하면 _start함수부터 실행이 될것이다. 따라가보장

그 전에 _start함수가 호출되기 전의 스택에는 hello의 argc, argv, env값이 들어가있다.(커널이 구성한다.)


_start함수는 그냥 인자(argv, argc,..)들을 스택에 넣어주고 __libc_start_main을 호출
(gdb로 따라가다보면 __libc_start_main를 처음 호출하는거라 바로 호출안하고 plt에서 got쓰는 다이나믹 링킹하고 호출)

int __libc_start_main(  int (*main) (int, char * *, char * *),
          int argc, char * * ubp_av,
          void (*init) (void),
          void (*fini) (void),
          void (*rtld_fini) (void),
          void (* stack_end));


env.
__libc_csu_init 호출


_init 호출


gmon 프로파일링 시스템초기화를 위한 call_gmon_start호출(필요하다면) 
익셉션 처리를 위한 unwinding stack을 설정하기 위해 frame_dummy를 호출 
생성자를 호출하는 __do_global_ctors_aux를 호출
(생성자가 코딩되어있으면 __do_global_ctors_aux안에서 호출)


__libc_start_main으로 돌아와서 main 함수 호출, hello 출력하고
exit호출, 소멸자 있으면 exit에서 소멸자 호출.



Read more

ANTI DEBUG 기법들(종명이형 강의 간단정리)

ANTI DEBUG
1. snapshot
     process context snapshot

2. checksum

3. packing
     section 권한 체크
     *virtualprotect - 권한 변경 함수
     암호화
     EP는 암호화를 푸는 루틴, 암호화 푸는 루틴이 실행되면서 암호화가 풀리면서 진짜 코드가
     쓰기, 실행가능한 영역에 써짐, 복호화된 코드의 시작이 OEP, 프로그램 실행!

4. garbage code
     prefix, code에 공백등 넣어서 디버거가 파싱하지 못하도록!

5. SEH
     강제로 exception 발생시켜 SEH에 정상루틴처리, 디버거가 물려있으면 SEH루틴 확인을 하기 힘듬

6. PEB
     InDebuggerPresent, FS[30]이용해서 디버거 물려있는지 확인

7. 난독화
     code 난독화로 해석하기 힘들게함

8. Time check (CPU clock check)

9. CC
     디버거의 특성을 이용해서 CC를 복사하는지 확인

10. 가상화
     가상으로 CPU를 만드는 개념
     55/8b ec를 push ebp, mov esp, ebp로 해석하지만 가상 cpu를 만들어서
     예를들면 66/92 d3를 push ebp, mov esp, ebp로 해석하도록 함

11. Control flow 난독화
     check값에 맞는 opcode를 수행, check++등을 이용해서 다음 opcode로 이동
     check 값을 전역변수로 놓으면 찾기 힘듬

*더미다는 10, 11을 기반으로 만들어진 패커
Read more

fedora 원정대 core 3 hell_fire -> evil_wizard

fedora 원정대 core 3 hell_fire -> evil_wizard

evil_wizard.c


hint 를 보면 GOT overwritting이 써있다. GOT overwritting 공격을 시도하여 쉘을 얻을 수 있다.

strcpy함수에서 overflow가 발생하여 eip를 원하는 곳으로 이동 시킬수 있게된다.
eip를 strcpy의 plt(고정주소)로 이동시켜 strcpy함수를 호출할 수 있고 ret에 poppopret가젯의 주소를 넣어 rop공격코드를 작성 할 수 있다.

strcpy를 이용하여 memset의 got에 system함수의 주소를 1바이트씩 복사 할 것이다. 그러면 memset을 호출하게되면 결국 memset의 got에 덮어씌여진 system함수를 호출 하게된다.

system함수에 인자로 들어갈 '/bin/sh'문자열도 위와 같은 rop 방식으로 strcpy함수를 호출하여 memcpy의 got에 복사하여 system함수의 인자로 넘겨 줄 것이다.

이제 필요한 주소들을 찾고 코드를 작성하면 된다.

먼저 poppopret가젯의 주소는


poppopret의 주소는 0x0804854f이다.

execve로 덮힘을 당할 memset@plt와 memset got를 찾는다.


memset@plt의 주소는 0x08048474이다.



got의 memset주소는 0x08049898이다.
got의 memcpy주소는 0x080498c0이다.


system의 주소는 0x007507c0이다.


strcpy@plt의 주소는 0x08048494이다.

필요한 got, plt주소는 구했으니

system의 주소, /, b, i, n, /, s, h가 있는 주소를 다 구하고 strcpy를 이용해서 1바이트씩 덮어 써야한다.


이렇게 전부다 뒤져서 익스플로잇 코드를 작성한다.


ret부분에 strcpy가 덮이고 인자로 덮힘당할 memsetgot의 주소와 덮을 문자의 주소가 들어간다. strcpy함수가 끝나면 poppopret 이 수행되어 인자 두개를 제끼고 다시 strcpy함수로 들어가는 rop이다.

공격을 하면


쉘을 얻을수 있다.

쉘코드:

Read more

fedora 원정대 core3 dark_eyes -> hell_fire

fedora 원정대 core3 dark_eyes -> hell_fire

hell_fire.c


sfp를 복사해 두었다가 원상복귀 시키고 fgets로 받은 문자를 strcpy하는 과정에서 스택을 조작할 수 있다. tcp 7777로 remote환경이다.

다른사람들의 라이트업을 참고해보니 do_system으로 eip를 이동시켜서 쉘을 얻는 것을 볼 수 있었다. do_system이 무엇일까..


system 함수에서 do_system을 호출하는 것을 볼 수 있었다.
system 함수는

#include <stdlib.h>

int system(const char *string)

이고 /bin/sh -c string을 호출하여 string명령어를 실행하고 fork()와 execve()의 조합으로 실행된다.


do_system함수를 까보면 함수의 밑부분에 sigaction(), sigprocmask()를 호출하고 execve()함수를 호출 하는 것을 볼 수 있다. eip를 sigaction쪽으로 이동시키면 쉘을 얻을 수 있는데 지식이 부족해서 왜 그런지 알수가 없다. execve가 실행되서 얻는것같은데 모르겠다



??

 (python -c 'print "A"*268 + "\x7f\x07\x75\x00"';cat) | nc localhost 7777
Read more

linux system call

linux system call

mlock() 메모리의 페이지 잠금
munlock() 메모리의 페이지 잠금 풀기
mlockall() 호출한 프로세스의 페이징을 금지시킨다.
munlockall() 호출한 프로세스에 대한 페이징을 다시 가능하도록 한다.
sched_setparam() 스케줄 파라미터 설정
sched_getparam() 스케줄 파라미터 설정값 가져오기
sched_setscheduler() 스케줄 알고리즘 파라미터 설정
sched_getscheduler() 스케줄 알고리즘 파라미터 값 가져오기
sched_rr_get_interval() 프로세스의 SCHED_RR간격을 가져온다
nanosleep() 지정한 시간에 실핼을 잠시 멈춘다.
mremap() 가상 메모리 주소를 재대응 시킨다.
poll() 파일 지정자로부터 이벤트를 기다린다.
chown() 파일 소유자 변경
getcwd() 최근 작업 디렉토리 가져오기
vfork() 자식 프로세스 생성, 부모 프로세스 주소공간에서 동작
clone() 자식 프로세스의 생성
setdomainname() 도메인 이름 설정
uname() 최근 커널의 정보 얻어오기
sigprocmask() POSIX 시그널 제어 관련 함수
create_module() 적재 가능한 모듈 엔트리 생성
init_module() 적재 가능한 모듈 엔트리 초기화
delete_module() 적재 모듈의 삭제
getpgid() 프로세스 그룹아이디 가져오기
readdir() 디렉토리 내용을 읽어들인다
select() 입출력 다중화
readv() 벡터를 읽는다
writev() 벡터를 쓴다
sys_getdis() 세션리더의 프로세스 아이디를 가져온다
sysctl() 시스템 파라미터를 읽고 쓴다
reboot() 리붓 시킨다
munmap() 메모리 페이지 해제
truncate() 파일의 길이 결정
ftruncate() 파일의 길이 결정
getpriority() 프로그램의 우선순위 얻어오기
setpriority() 프로그램의 우선순위 설정
socketcall() 소켓 시스템 콜
syslog() 커널 메시지 버퍼의 내용을 읽거나 클리어 한다
setitimer() 내부 타이머 설정
getitimer() 내부 타이머 값 가져오기
wait4() 프로세스릐 종료를 기다린다. BSD스타일
swapoff() 파일/장치의 스와핑 끝내기
sysinfo() 시스템의 정보 얻어오기
fsync() 파일의 내부상태와 디스크상의 상태를 동기화 한다
sigreturn() 시그널 핸들러와 클린업 스택 프레임으로 부터 반환
sigaction() POSIX 시그널 제어 함수
sigsuspend() 시그널 마스크를 일시적으로 대체한 후 시그널을 기다린다.
sigpending() 시그널을 블럭하고 검사를 수행한다.
sethostname() 호스트이름 설정
setrlimit() 자원의 제한값을 설정한다.
getrlimit() 자원의 제한값을 얻어온다
gettimeofday() 날짜와 시간을 얻는다
settimeofday() 날짜와 시간을 설정한다
getgroups() 포함된 그룹 아이디의 목록을 얻는다
setgroups() 포함될 그룹 아이디의 목록을 설정한다.
symlink() 파일에 대한 심볼릭링크 생성
lstat() 파일의 상태 얻기
readlink() 심볼릭 링크의 연결된 파일 이름을 읽는다.
uselib() 공유라이브를 선택한다.
swapon() 파일과 장치의 스와핑을 시작한다.
dup() 열린 파일 지정자를 복사한다.
pipe() 내부 통신을 위한 채널을 생성한다.
times() 프로세스 시간을 얻는다.
setgid() real 그룹 아이디를 설정한다.
getgid() real 그룹 아이디를 얻어온다.
geteuid() effective 유저 아이디 가져오기
getegid() effective 그룹 아이디 가져오기
ioctl() 장치 제어
fcntl() 파일 제어
umask() 파일 마스크의 생성
chroot() 루트디렉터리의 변경
dup2() 파일 지정자 복사
getppid() 부모 프로세스의 PID얻기
getpgrp() 프로세스의 그룹 아이디 얻기
setsid() 세션과 프로세스 그룹 아이디 설정
chown() 파일의 소유자 변경
stat() 파일의 상태 얻기
lseek() 파일에서의 위치 변경
getpid() 프로세스의 ID얻어온다
setuid() 실제 유저 아이디 설정
getuid() 실제 유저 아이디 얻어오기
alarm() 실정시간후 alarm시그널이 전달되도록 한다
fstat() 파일 상태 얻기
pause() 시그널이 전달될 떄까지 대기한다.
access() 파일의 권한을 검사한다.
sync() 슈퍼블럭을 업데이트 한다
kill() 프로세스에 시그널을 전송한다
rename() 파일의 이름과 위치를 변경한다
mkdir() 디렉터리를 생성한다
rmdir() 디렉터리를 제거한다.
exit() 현재 프로세스의 종료
fork() 자식 프로세스의 생성
read() 파일 디스크립터로부터 읽기
write() 파일 디스크립터로 쓰기
open() 파일이나 장치열기
close() 파일 디스크립터 닫기
waitpid() 프로세스의 종료를 기다린다
creat() 파일이나 장치의 생성
link() 파일을 위한 새로운 이름 만들기
unlink() 파일 혹은 참조된 이름을 삭제한다
execv() 프로그램의 실행
chdir() 작업 디렉터리의 변경
time() 초 단위의 시간 얻기
mknod() 일반 혹은 특수파일의 생성
chmod() 파일의 권한 변경하기
Read more

fedora 원정대 core3 iron_golem -> dark_eyes

fedora 원정대 core3 iron_golem -> dark_eyes

dark_eyes.c


sfp를 백업 해두었다가 오버플로우가 발생한 이후에 sfp를 원상복귀 시킨다.
sfp는 사용할 수 없고 ret를 조작 할 수 있다. hint에 RET sleding이 써있다.
RET sleding을 사용하여 스택영역을 벗어난 후에 execve함수를 실행하면 execve함수의 인자로 고정적인 문자열을 넣을 수 있다.그 문자열을 이용하여 쉘을 얻어본다.


간단하게 쉘을 얻을수 있는 프로그램을 작성하고 컴파일한다. 위의 dark_eyes의 권한으로 execve를 이용해 위의 쉘을 얻는 프로그램을 실행하면 쉘을 얻을 수 있다.

ret와 execve함수의 주소를 찾아본다.

execve
ret

ret sleding을 태워서 스택을 벗어난 후 execve를 실행하도록 코드를 작성한다.

./dark_eyes `python -c 'print "A"*268 + "\xfe\x82\x04\x08"*8 + "\x90\x54\x7a\x00"'`

이 코드를 돌리면 execve의 인자로 이상한 것이 들어가서 에러가 발생한다.
strace를 사용하여 execve의 인자로 들어가는 고정바이트를 찾을 수 있다.

strace ./dark_eyes `python -c 'print "A"*268 + "\xfe\x82\x04\x08"*8 + "\x90\x54\x7a\x00"'`


execve로 알수 없는 문자열이 들어간다. strace의 표준에러출력을 리다이렉션으로 저장하여 xxd를 사용하면 execve로 들어가는 문자열의 헥사값을 알 수 있다.

위의 쉘을 얻는 프로그램에 execve로 들어가는 문자열로 심볼릭 링크를 걸어주고 위의 쉘코드를 실행하면 쉘을 얻을 수 있다.


Read more