본문 바로가기
해킹/시스템해킹

로지컬 버그

by 맑은청이 2020. 8. 6.
728x90
반응형

https://dreamhack.io/

 

해커들의 놀이터, DreamHack

해킹과 보안에 대한 공부를 하고 싶은 학생, 안전한 코드를 작성하고 싶은 개발자, 보안 지식과 실력을 업그레이드 시키고 싶은 보안 전문가까지 함께 공부하고 연습하며 지식을 나누고 실력 향

dreamhack.io

 

저는 드림핵에서 강의를 수강하고 있습니다. 

 

로지컬는 지난번에 배웠던 메모리커럽션과는 달리 메모리를 조작하는 것이 아닌 프로그램의 논리적 오류를 이용합니다. 

https://com24everyday.tistory.com/225

 

시스템해킹 메모리 커럽션

메모리 커럽션(Memory Corruption) , Corruption 은 구글에 치니 부패라는 뜻이 나오네요. 메모리를 오염시키는거니깐 대충 뜻이 맞는거 같습니다. https://com24everyday.tistory.com/224 시스템 해킹 기초 취약..

com24everyday.tistory.com

 

여기서 논리적 오류랑 프로그램이 부정확하게 동작하지만 크래시가 동작하지 않는 경우 입니다. 그렇기 때문에 발견도 힘들고 오래 프로그램 내에 존재할 수 있는 거죠. 

 

통장 출금 시스템에 음수에 대한 관리가 없으면 1000 - (-10000) = 11000 이렇게 잔고를 늘리는 어마어마한 오류가 발생합니다. 이런 게 로지컬 버그입니다. 이에 관해 배워 보겠습니다. 

 

 

Command Injection 

인젝션은 공격자의 입력을 셸 커맨드 혹은 쿼리 일부로 처리하는 겁니다. 

이 중에 SQL 인젝션이 가장 대표적이라고 할 수 있습니다. 

이는 프로그램이 입력에 대한 적절한 검증을 거치지 않기 때문에 발생합니다. 

 

공격자는 메타문자와 같은 특수한 문자로 임의의 코드 실행까지 이어지게 할 수 있습니다.

 

$ : 쉘 환경변수

&& : 앞뒤 명령어 실행

; :명령 구분자

| :명령어 파이핑

* : 와일드 카드 

` : 명령어 치환 

 

ex ) ping -c 127.0.0.1; /bin/bash

 

 

Race Condition 

: 프로세스 스레드 간 자원 관리 실수로 발생하는 상태  

서로 다른 스레드에서 뮤텍스가 걸려 있지 않아 공유 메모리에 접근하는 경우 프로그램의 가정을 파괴할 수 있습니다. 

이는 운영체제에서 배웠던 내용 같네요!

 

다음과 같은 두 함수가 두 스레드에서 실행될 때는 버퍼 오버플로우가 발생할 수 있습니다. 

input() 함수가 실행될때 if 문을 통과했는데 거기서 count() 가 실행되면 read() 에서 버퍼오버플로우가 발생하게 되는 겁니다. 

void input(){
	char buf[20];
    len += 20;
    if(len > sizeof(buf)){
    	exit(0);
    }
    read(0,buf,len);
}
void count(){
	len += 20;
}

 

전역 변수를 여러 스레드가 참조할 때는 답이 달라질 수도 있다는 걸 주의 해야합니다. 

 

Path Traversal

: 프로그래머가 가정한 디렉토리를 벗어나 외부에 존재하는 파일에 접근할 수 있는 취약점 

../ 을 이용해서 상위 디렉토리의 있는 파일들을 읽을 수 있습니다. 

이 과정에서 '/etc/passwd/' 와 같은 중요한 파일들도 노출이 될 수가 있는거죠.

 

Environment attack

환경 변수 공격

환경변수는 프로세스가 동작하는 방식에 영향을 미칠 수 있는 동적인 값들의 모임입니다. 

export 를 통해 환경 변수를 출력할 수 있습니다. 

 

예를 들면 사용자의 이름을 담고 있는 USER 환경 변수는 사용자가 바뀔 때마다 변화합니다. 

 

 

명령어를 실행할 때마다 절대 경로를 넣는거는 너무 귀찮은 일이기 때문에 리눅스에서는 PATH 라는 환경변수를 제공합니다. PATH 환경 변수에 경로를 넣어두면 해당 경로에 있는 파일을 현재 디렉토리에 있는 파일과 같이 실행할 수 있습니다. 

 

다음 에제는 PATH 를 비워 줬다가 다시 절대경로를 넣어주는 과정입니다. 

PATH 를 비운 다음 id 를 입력하면 그런 파일이 없다는 문구가 뜨는 것을 알 수 있습니다. 

 

// gcc -o environ1 environ1.c
#include <stdlib.h>
#include <unistd.h>
int main()
{
    printf("Screen Cleaner\n");
    system("clear");
         
    return 0;
}

 

다음 코드에서는 clear 명령어가 system 함수의 인자로 전달됐기 때문에 PATH 환경 변수를 읽어들여 해당 경로에 있는 clear 파일을 찾을 겁니다 . 

그러니 이 PATH 를 조작하면 원하는 파일을 실행시킬 수 있겠죠? 

 

/bin/sh 파일을 심볼릭 링크를 사용해서 clear 이름으로 링킹하면 어떻게 될까요?

 

ln -s /bin/sh ./clear

 

그 다음에는 PATH 환경 변수를 비워줍니다. 

 

export PATH=""

 

그리고 environ1.c 를 실행합니다. 

 

그럼 코드 앞에서 clear 를 실행시켰을때 /bin/sh/ 를 실행시켜 셸을 획득한 것을 확인할 수 있습니다.

 

특정 명령어를 사용해야 할 경우에는 절대 경로를 사용하여 익스플로잇이 불가능 하도록 만드는 것이 좋은 코드의 예이다. 

 

 

환경 변수 공격 - LD_PRELOAD

 

LD_PRELOAD 환경 변수를 통해 로더가 프로세스에 로드할 라이브러리 파일을 지정할 수 있습니다. 

프로그램에서 특정 함수를 호출하면 해당 환경 변수에 등록된 라이브러리 파일을 먼저 참조하여 호출된 함수를 찾게됩니다. 

 

// gcc -o libc.so libc.c -fPIC -shared
#include <stdlib.h>
void read() {
	execve("/bin/sh", 0, 0);
}

execve 는 실행가능한 파일인 filename 의 실행코드를 현재 프로세스에 적재하여 기존의 실행코드와 교체하여 새로운 기능으로 실행합니다. 즉 새로운 코드로 메모리에 loading 하여 처음부터 실행합니다.

#include <unistd.h> 
int execve(const char *filename, char *const argv[], char *const envp[]);

 위 코드를 실행하면 libc.so 라는 공유 라이브러리 파일이 생성됩니다.  LD_PRELOAD 환경 변수에 libc.so 를 전달하면 

모든 파일을 실행할 때 해당 파일을 참조합니다. 

 

 

// gcc -o environ2 environ2.c 
#include <unistd.h>
#include <stdio.h>
int main()
{
	char buf[16];
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 2, 0);
	write(1, "Data:", 5);
	read(0, buf, sizeof(buf)-1);
	
	write(1, "GOOD", 4);
        return 0;
}
#include <stdio.h>  // C++ 에서는 <cstdio>

int setvbuf(FILE* stream, char* buffer, int mode, size_t size);

이는 스트림 버퍼링 방식을 변경합니다. 

버퍼를 특정한 스트림의 입출력 연산에 사용할 수 있도록 변경하는데 이 함수는 버퍼링 방식과 버퍼의 크기를 설정할 수 있도록 지원합니다. 

그럼 이런 식으로 공유 파일로 전달도니 'libc.so' 가 로딩된 것을 알 수 있습니다. 

 

 

LD_PRELOAD 환경 변수는 로더가 프로세스에 로드 할 라이브러리 파일을 지정할 수 있습니다. 프로그램에서 함수가 호출되면 제일 먼저 LD_PRELOAD 환경 변수에 등록된 라이브러리 파일을 참조하여 호출합니다. 라이브러리를 새로 생성하여 프로그램에서 호출하는 함수의 이름과 동일한 함수를 만들고 LD_PRELOAD 환경 변수에 해당 라이브러리 파일을 전달하면 원하는 코드를 실행할 수 있습니다.

728x90
반응형