목록System Hacking (12)
Information Security
Shell Code -버퍼 오버플로우 공격의 핵심은 오버플로우가 발생하는 버퍼에 저장되는 공격자의 코드로 실행 제어를 이동시키는 것이다. -사용자 명령어 라인의 해석기 Shell로 제어를 넘기고 공격당한 프로그램의 권한으로 시스템의 다른 프로그램에 접근하기 때문에 Shell code라고 부른다. 일단 /bin/sh 명령어를 실행하는 소스 코드를 작성했다. 메모리에 실행할 명령어만 올려야 하므로 data 세그먼트는 사용하지 않고 기계어로 변환해야 한다. text 세그먼트만 사용해서 어셈블리어로 작성했다. 실행 파일을 disassemble해서 기계어를 획득했다. 그런데 여기서 보이는 null 값(00)은 입력할 수 없는 값(bad character)이다. 1. 4바이트 레지스터 eax에 1바이트 값을 넣으면..
권한 상승 기법 -스택 버퍼 오버플로우 공격은 보통 SetUID(Set User ID)가 설정된 root 권한의 프로그램을 공격대상으로 한다. -> 스택에 정해진 버퍼보다 큰 공격 코드를 반환주소를 변경함으로써 임의의 공격 코드를 root 권한으로 실행하도록 한다. 권한 상승 기법을 공부하기 위해 OverTheWire의 Leviathan 문제를 풀어볼 것이다. 다음 레벨로 접속하는 패스워드를 찾아야 하고 /etc/leviathan_pass 디렉터리에 패스워드가 저장되어 있다. ★ leviathan1 leviathan1에 접속해서 파일 목록을 확인했더니 check 실행 파일이 존재한다. -> 특수 권한 SetUID가 설정되어 있다. 특수 권한에 의해 check 프로그램이 실행되는 중에는 소유자(leviat..
스택 버퍼 오버플로우(stack buffer overflow) -스택에 정해진 버퍼보다 큰 공격 코드를 삽입하여 반환 주소를 변경함으로써 임의의 공격 코드를 루트 권한으로 실행하도록 하는 방법이다. -취약한 함수 scanf, gets, strcpy, ... 등을 사용해서 발생하는 취약점이다. 1. 취약점 확인 - strcpy 함수 오버플로우 공격을 시도하기 위해서 취약한 코드를 작성 했다. strcpy(dest, src); strcpy 함수로 인해 입력받은 인자가 buffer2로 복사된다. 인자를 전달하면서 파일을 실행하면 buffer2의 값이 출력된다. 이번에는 길이가 긴 인자를 전달했다. -> 그랬더니 buffer1의 값이 출력되었다. ??? strcpy( buffer2, argv[1] ); 이 부분..
시스템 콜을 이용한 mkdir 프로그램 작성 실습 -시스템 콜만 가지고 리눅스의 mkdir 명령어와 비슷하게 프로그램을 구현해볼 것이다. -필요한 시스템 콜 함수는 다음과 같다. -> read: 경로 입력 -> write: 디렉터리 생성 성공, 실패 출력 -> mkdir: 디렉터리 생성, 성공시 0 반환 1. C언어로 작성 먼저 C로 작성하고 어셈블리어로 변환할 것이다. 일단 read 함수로 경로를 입력받는다. -> fd = 0 (표준 입력) read 함수는 입력받은 문자열에 자동으로 개행 문자가 들어가게 된다. 경로에는 개행 문자(\n)가 들어갈 수 없기 때문에 제거해야 한다. while 문을 이용해서 개행 문자를 찾으면 0으로 값을 변경하도록 한다. 다시 확인해보면 개행이 되지 않는 것을 확인할 수 ..
System Call -커널은 하드웨어 특성으로부터 프로그램들을 격리시키고, 하드웨어와 직접 상호 작동하며 프로그램에게 일관된 서비스를 제공한다. -Shell(user)은 정의된 System Call에 의해서 커널과 통신한다. 1. C 언어에서 시스템 콜 사용 -라이브러리에 이미 시스템 콜과 동일한 Wrapper 함수가 존재한다. printf 함수 대신에 write 시스템 콜 함수를 이용해서 문자열을 출력할 것이다. # man 2 write -> write 함수의 원형 확인 write(fd, 출력할 문자열, 문자열의 개수) -> fd = file descriptor -> fd : 1(표준 출력) -> 공백, 개행 문자 포함 문자열의 개수 : 13 2. 어셈블리어에서 시스템 콜 사용 -어셈블리어에서는 호출..
우리는 메인 함수를 정의할 때 int main()을 사용하지만 사실 메인 함수의 원형은 다음과 같다. int main(int argc, char *argv[]); saved ebp saved eip argc *argv 여기서 ebp는 main 함수의 기준점이다. argc, *argv는 main 함수가 호출되기 전에 항상 존재하고 있다. argc는 인자의 개수를 저장하고 있다. 값을 확인해보면 인자를 입력하지 않아도 무조건 `1이다. -> 실행 파일명(./a.out)도 인자로 간주하기 때문이다. 프로그램을 실행할 때 main 함수로 전달되는 인자를 space로 구분해서 지정할 수 있다. -> 실행 파일 + 3개의 인자 = 4 위의 C 코드를 어셈블리어로 변환한 것이다. -> argc는 main 함수의 기준..
함수 어셈블리어에서 함수라는 개념은 없다. 하지만 함수처럼 동작하도록 구현할 수는 있다. 어셈블리어에서 함수를 구현해보기 위해서는 스택 메모리에 대한 이해가 필요하다. 프로세스가 사용하는 메모리의 구조를 알아보기 위해서 간단한 C코드를 작성했다. C코드를 컴파일한 후, 실행 파일을 실행한다. -> sleep에 의해서 대기 상태가 된다. -> Ctrl+Z로 빠져나온 후 ps -ef로 프로세스 목록을 확인한다. 백그라운드로 실행 중인 것을 알 수 있다. 여기서 프로세스의 PID를 확인한다. (817) proc 디렉터리에는 이름이 PID인 디렉터리들이 존재한다. -> 우리는 PID 817 디렉터리를 확인해야 한다. 프로세스는 파일 형태로 저장되어 있고, 그에 대한 정보도 파일이다. -> 817 디렉터리 중 m..
관계 연산: cmp(compare) cmp는 플래그 레지스터의 값을 변경하기 위한 명령어이다. cmp vleft, vright sub vleft, vright 연산을 수행한 결과 값에 따라 플래그 레지스터를 변경한다. 1) vleft가 더 크면 sub vleft, vright의 결과는 양의 정수가 나온다. -> SF(Sign Flag)의 값은 0, ZF(Zero Flag)의 값은 0으로 변경된다. 2) vright가 더 크면 음의 정수가 나온다. (부호 발생) -> SF의 값은 1, ZF의 값은 0으로 변경된다. 3) vleft와 vright가 같으면 0이 나온다. -> SF의 값은 0, ZF의 값은 1로 변경된다. pushfd로 EFLAGS 레지스터를 확인할 수 있다. cmp 20, 30 sub 20,..
레지스터(Register) CPU가 연산에 필요한 데이터를 기억하는 고속의 임시 기억 장치이다. 용도별로 여러 개의 레지스터가 존재한다. 속도 레지스터 >> 메모리(주기억장치) >> 보조기억장치(디스크, USB) 1) 범용 레지스터 EAX(Extended Accumulator Register) EBX(Extended Base Register) ECX(Extended Counter Register) EDX(Extended Data Register) 2) 포인터 레지스터 ESI(Extended Source Index) EDI(Extended Destination Index) -> 문자열 복사(Source -> Destination)에 이용 EBP(Extended Base Pointer) ESP(Extende..
주소 vs 메모리 C언어는 포인터를 통해 메모리에 대한 직접 접근이 가능하지만, 어셈블리어는 변수를 선언해서 사용하지 않고, 메모리를 직접 사용한다. 주소는 메모리 상의 위치(정수)를 나타낸다. 파일에서 오프셋과 동일한 의미를 가진다. 메모리는 데이터가 들어있는 실제 공간이다. ★ C언어 코드를 어셈블리어로 변환하기 int a; int b; int main() { scanf("%d", &a); scanf("%d", &b); printf("a: %d", a); printf("b: %d", b); return 0; } 두 변수를 입력받고 입력받은 변수를 출력하는 C언어 코드이다. 코드에서 사용할 함수는 printf, scanf 두 개이다. extern printf extern scanf extern을 통해 ..