코딩하는 개굴이

시스템 해킹 본문

~2019/해킹맛보기 스터디

시스템 해킹

개굴이모자 2016. 12. 26. 17:00
반응형

시스템 해킹

 

시스템 해킹은 운영체제 해킹이라는 의미로 주로 받아들여지며, 접근 권한이 없는 컴퓨터의 자원에 대해 접근하거나 정보를 유출하는 모든 기법을 총칭한다. 그 중에서도 소프트웨어를 공격하기 위한 메모리 조작 기법들을 주로 의미한다.

 

시스템 해킹 기법으로는 정형화 된

명령어 삽입 공격/레이스 컨디션/메모리 오염공격

이 있다.

 

 

해킹의 일반적인 목적

1.     접근 권한이 없는 컴퓨터에 명령을 내린다

2.     현재 접근 권한이 있는 시스템에서 더 높은 권한을 얻는다

 

시스템 해킹의 접근

1.     높은 권한으로 실행중인 프로그램의 실행 흐름을 바꾼다

2.     실행할 때 자동으로 높은 권한을 부여 받는 프로그램의 실행 흐름을 바꾼다

 

 

환경구축

시스템 해킹은 프로그램 분석, 공격 아이디어 도출, 공격코드 작성으로 이루어진다.

툴로는, windbg, IDA, gdb, 올리디버거 등이 있고, 공격코드를 작성하기 위한 언어로는 주로 파이썬을 사용한다

 

명령어 삽입 공격

명령어 삽입 공격은 기존의 명령어들을 재사용하거나 쉘 명령어들을 이용해 특정 기능을 수행할 때 발생할 수 있다

 

기존의 소프트웨어나 소프트웨어를 구축했던 지식을 활용해 새로운 소프트웨어를 개발하는 일을

코드 재사용 또는 소프트웨어 재사용이라고 한다.

짧은 소스 코드를 재사용하는 것은 꽤 오래전부터 행해져 왔다. 코드 재사용을 통해서 어떤 기능

을 수행하는 부분의 시간을 절반 이상으로 단축할 수 있기 때문이다.

어려운 기능의 경우, 코드를 재사용하지 않고, 기본적으로 제공해 주는 기능만 이용하게 된다면,

돌아가야 하므로, 코드가 훨씬 복잡해 질 것이다.

이때 원하는 기능을 수행하는 외부 프로그램이 있으면, 외부 프로그램을 수행한 그 결과를 그대

로 사용하면 된다. 이를 통해 재사용처럼 개발시간을 많이 단축할 수 있다.

이를 위해, 많은 프로그래밍 언어들이 외부 프로그램을 수행하기 위한 방법으로 텍스트 기반

명령어처리기를 이용한다.

 

C언어에서는 외부 프로그램을 수행 할 수 있는 함수로, system()함수를 제공하는데, 이 함수는 운영체제의 기본 명령어 처리기로, 인자로 들어온 문자열을 실행하고, 그 결과를 출력한다. system() 함수를 이용해 dir명령어를 수행함으로써 똑 같은 기능을 수행하는 프로그램을 더 빠르게 작성할 수 있다.

명령어 삽입 취약점은 명령어 처리기를 통해 명령을 수행 할 수 있는 함수가 있다면 발생할 가능성이 있다.

 

 

각 언어별 명령어 실행 함수

C/C++   운영체제 공통: system(), popen(), execlp(), execvp()

           윈도우 전용: ShellExecute, CreateProcess_wsystem()

Perl       System(), exec(), backticks(‘), vertical bar operator(|),  eval(), 정규 표현식 /e 연산자

파이썬   exec, eval, os.system, os.open, execfile, input, compile

자바      Class.forName(string name), class.newInstance, Runtime.exec()

C#        System.Diagnostics.Process.Start()

 

 

명령어 삽입 공격에 사용되는 명령어 처리기 연산자

연산자

사용법

의미

>

prog>file

prog수행결과로 나오는 표준 출력을 file로 저장. 기존 파일은 내용을 지우고 새로 작성

>>

prog>>file

prog수행 결과로 나오는 표준 출력 file뒤에 추가한다

<

prog<file

File의 내용을 prog의 표준 입력으로 전달한다

|

prog1 | prog2

prog1의 표준 출력을 prog2의 표준 입력으로 전달한다

&&

prog1&&prog2

prog1의 명령 수행이 성공적이면 prog2를 호출한다

||

prog1 || prog2

prog1 명령이 실패하면 prog2를 호출한다

;(linux)

prog1 ; prog2

prog1을 수행한 후 prog2를 수행한다

&(linux)

prog &

prog1&prog2

prog를 백그라운드에서 수행한다

prog1을 백그라운드에서 수행하고 prog2를 수행한다

$()(linux)

$(command)

해당 구문이 command를 수행한 결과로 치환된다

&(window)

prog1&prog2

prog1을 수행한 수 prog2를 수행한다

‘(linux)

‘command’

해당 구문이 command를 수행한 결과로 치환된다

 

 

레이스 컨디션

Race Condition Attack(경쟁 상태 공격)서로 다른 프로세스나 스레드가 같은 자원을 공유할 때 실행 순서에 따라 결과가 달라질 수 있는 현상을 이용해 공격하는 기법이다.

 

운영체제에서 여러 연산과 프로그램을 동시에 처리할 수 있는 특성을 병행성이라 한다. 이것이 멀티 코어 CPU구조가 널리 이용된 이후, 소프트웨어의 병행성은 비약적인 성능 향상을 하였다.

 

 

이 프로그램을 수행하면,

 

 

이와 같은 결과를 얻을 수 있는데, 병행성을 이용해서, 이 프로그램이 더 빠르게 동작하도록 할 수 있다.

CPU의 개수별로 스레드를 만들어서 각각의 부하를 1/(코어의 개수)로 줌으로써 기존에 하나의 CPU에서만 동작함에 따라 생기는 부하를 줄일 수 있다. 그리고 같은 메모리 변수(total count)를 사용함으로써 발생하는 부하를 줄이기 위해, 계산 과정의 중간 결과값을 지역변수에 저장해서 연산이 끝났을 때 이를 반영하도록 했다.

 

MyThreadFunction 함수는 스레드 개수에 맞춰서 계산량을 분담해 계산한다.

위에서는 4개의 스레드를 생성해 100번씩 증가연산을 수행한다.

이렇게 메인 함수를 수정하고 프로그램을 실행하면, 마지막줄이 처

음에 실행한 프로그램과는 달리 4*100=400이 아니라 300인 것을

 알 수 있다. 답이 다른 이유는 MyThreadFunction() 함수의 첫번째 

줄에서 temp변수에 들어가는 입력에 따라 total_count_의 결과값

이 결정되는데, 첫번째 스레드와 두번째 스레드 둘다 total_count_

로부터 같은 값을 받기 때문에 하나의 스레드를 수행한 결과만이 

반영 된다. 결국 두 스레드를 수행하고도 하나의 스레드만 수행한 

효과를 얻게 된다. 이런 식으로 여러 스레드가 하나의 변

수 total_count_를 공유하여서 여러 프로세스나 스레드가 수행되는 

순서에 따라 수행 결과가 달라지는 현상을 레이스 컨디션이라고 한

.

 

 

 

메모리 오염 공격

 

메모리 오염 공격은 정상적인 프로그램 수행을 방해하는 메모리 조

작으로 로그램이 개발자의 의도와는 다르게 메모리를 쓰거나 읽

는 행위를 말한다. C언어를 기계어로 변환하는 과정에서 문맥손실

이 발생할 수 있다.

 

이 소스를 보면 a변수가 정수형으로 선언 되었다는 것을 알 수 있

지만, 만약 컴파일 된 코드를 리버싱 하는 경우, 대개 변수 a, b가 모

두 문자열이라고 분석한다. 이것은 프로그래밍 언어의 소스코드가 

기계어로 번역되면서, 이런 문맥에 대한 정보를 비워 버리기 때문

이다.

 

 

개발자가 처음 프로그램의 구조를 설계할 때 결함을 발견하는 경우를 디자인 결함이라고도 부르는데, 프로그래머가 의도한 바를 구현하는 방법 자체에 문제가 있는 경우이다.

 

버퍼 오버플로우 공격

 

메모리는 제한적인 자원이기에, 효율적인 사용을 위해 배열 변수를

 선언할 때 프로그래머가 크기를 지정하도록 한다. 버퍼오버플로우

프로그래머가 사용하려고 할당한 메모리보다 더 많은 양의 메모

리를 덮어 쓸 때 발생하는 버그.

버퍼오버플로우가 발생하는 원인을 크게 두가지 조건으로 나눠서

첫번째는 버퍼오버플로우가 발생하는 메모리 영역을 기준으로 분

하는 것이고, 두번째는 취약한 코드 패턴에 따른 분류이다.

 

버퍼오버플로우의 가장 기본은 인접한 메모리 영역을 덮어쓰는 것이다. 현재 사용하는 버퍼 뒷부분에 더 많은 데이터를 넣는 것이다. 버퍼가 특정 데이터의 크기를 입력  받거나 복사할 때 더 많은 영역을 복사하게 되면 버퍼 오버플로우가 발생한다.

 

오브 바이 원 버그

오브 바이 원 버그는 1의 차이로 인해 오동작하는 논리 오류를 말한다.

요즘 컴파일러에서 자동으로 변수 사이에 임의의 바이트를 집어넣는 패딩이라는 개념 때문에 자주 발생하는 조건이 필요하지만, 오래된 프로그램이나 컴파일러를 사용하는 프로그램에서는 종종 발견할 수 있다.

 

더블프리 버그

더블프리 버그는 free()함수가 두 번 이상 같은 메모리에 호출되었

을 때 발생한다.

이 현상은 프로그램 구조가 복잡한 탓에 메모리를 어디서 해제해야

 되는지를 프로그래머가 착각해 코드 배치를 잘못 하였을 경우

예외 처리에서의 실수를 악용한 공격자가 의도적인 에러를 유발해

 프로그램 버그를 만들 수 있다. 같은 메모리 번지가 중복되어서 

free()가 되면, 일정 환경에서 버퍼 오버플로우 현상이 발생할 수 있

.

 

è이게 똑 같은 인자에 free()를 두 번 수행하면 동적 메모리를 관리하는 자료구조가 오동작하게 되는데, 일정 환경에선 malloc()이 두 번 같은 메모리를 리턴할 수 있게 되고, 이를 이용해 공격자가 버퍼오버플로우 현상을 일으킬 수 있다. 한 메모리가 서로 다른 두 스트링으로 사용되는데, 한 곳에서는 길이를 a, 다른 한 곳에서는 길이를 b로 제한해서 사용한다고 하자. 이때 큰 쪽으로 메모리를 채우고, 작은 쪽에서 사용하게 되면, 상대적인 길이 차이로 인해 버퍼 오버플로우를 발생시킬 수 있다.

 

스택 영역

스택 세그먼트는 CPU가 프로그램 함수를 실행하는 동안 필요한 정보를 저장하는 메모리 영역이다. 스택 기반의 오버플로우는 함수의 지역변수가 저장되는 메모리에서 주로 발생한다.

https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%83%9D_%EB%B2%84%ED%8D%BC_%EC%98%A4%EB%B2%84%ED%94%8C%EB%A1%9C

 

 

데이터 영역

데이터 세그먼트는 프로그래밍 시 전역적으로 사용되는 정보, 즉 전역적으로 사용되는 변수 중 초기값을 가지고 있는 변수들을 저장하는데 사용하는 메모리 영역이다. 주로 전역변수와 정적변수를 저장하는데 사용된다.

데이터 세그먼트는 초기값을 가지고 있는 변수가 저장되는 데이터 영역과, 초기값이 없는 영역인 데이터 영역으로 구분할 수 있다.

 

힙 기반 버퍼 오버플로우

힙 세그먼트는 프로그램 수행 시 동적으로 할당하고 해제하면서 사

용할 메모리를 말한다. 가장 기초적인 힙 기반의 오버플로우는

접한 메모리 청크(동적으로 메모리를 할당하고 해제할 때 사용하

는 메모리 단위) 서로 덮어쓰는 패턴으로, 연달아 할당받은 메모

는 서로 인접할 확률이 높으므로, 처음 할당받은 메모리 청크 부

을 덮으면, 뒷부분에 해당하는 메모리 청크 부분에 데이터를 쓸 

수 있다.

 

책은 컴파일러 설정, 선언 순서에 따른 지역변수의 메모리 배치 순서(지역변수는 마지막에 선언할수록 앞부분에 배치가 된다), 올리디버거 JIT디버거 설정에 대해 언급하고 있다.

 

형식 문자열

형식문자열은 출력값을 일정한 양식에 맞게 통일시키기 위해 사용

하는 규격을 정하는 문자열이다. 밑의 예제에서는 “%20s %s\n”과 

“%20s %d\n”%[width]type의 문법으로 출력값이 적절한 공백으

로써 보기 좋게 출력을 해주는 형식 문자열이다.


è원래 책의 예제가 C++을 사용해서, C를 사용한 것으로 바꾸어 보았습니다

 

형식문자열 취약점은 형식문자열을 사용하는 함수의 입력을 사용자가 임의로 조작할 수 있을 때 발생할 수 있는 문제점이다

 

 

문자열을 출력하는 프로그램에서 일반적인 입력을 넣으면 프로그

램이 정상적으로 동작하지만, 형식문자열을 넣으면, 엉뚱한 값으로 

바뀌는 것과 같은 취약점이 발생하곤 한다.


 

 

 

정수 오버플로우/언더플로우 공격

일반적인 오버플로우는 strcpy(), memcp() 등의 경계값을 제대로 체

크하지 않은 변수 복사의 문제를 이용한 공격법이었다. 연산결과

가 저장하려는 데이터 타입이 표현할 수 있는 최대값보다 크면 오

버버플로우, 최소값보다 작으면 언더플로우라고 한다.


 

이 소스코드에서 a+b의 값은 둘의 합인 2469135780이 나오지 않고,

 

위와 같은 결과가 나온다. 이는 실제 연산값의 결과를 16진수로 바

꾸고, 2의 보수를 취한 값이 되는 것인데, 이와 같이 몇몇 특수한 상

황에서 발생하는 정수형 오버플로우가 다른 기술들과 결합 해 큰 

문제를 일으킬 수 있다.

 

 

실전 요약

è실습이 안 되어서 일단 내용 이해한 것을 요약해 보았습니다.

취약점을 공격하기 위해서는 입력 버퍼에 들어가면 안되는 데이터를 식별해야 하는데, 취약점이 발생하는 버퍼에는 일반적으로 허용되지 않는 3개의 데이터인 널바이트(“\x00”);, 라인피드(“\x0a”);, 캐리지 리턴(“\x00”)을 입력할 수 없다.

메모리 오염 공격을 하는 첫 단계는 프로그램이 실행 중에 죽는 현상을 재현하는 POC 코드를 작성하여, 이를 점진적으로 계속 정제해 더욱 정교한 코드를 만들 수 있다. POC 코드를 통해 EIP에 원하는 테이터를 쓰고, 그것을 확인 한 후, 입력한 버퍼 중 어느 부분이 EIP로 덮어써지는지 확인하기 위해 기존에 보냈던 버퍼 크기와 똑같이 메타스플로잇 패턴을 이용해 buffer 변수에 있는 내용을 다시 보낸다.

 

http://blog.naver.com/lovechojh03/220886237147

위의 실습들이 너무 어려워서 C로도 해볼 수 있는 간단한 버퍼 오버플로우 실습을 해보았습니다.

 

이렇게 변수들은 스택으로 적제가 되는데, 낮은주소에서 높은 주소로 쌓인다.

위 소스 코드를 보면, 변수 a0이 아니라면 인증성공을 띄우도록 코딩이 되어있는데, 이는 결국 변수 a에 있는 값이 0만 아니라면 되는 것이므로, 변수 a0이 아닌 a1,2,3과 같은 아무 글자나 들어가도 된다는 것이기 때문에, char 변수의 크기는 1바이트이고, char passwd[5]는 배열로 1바이트씩 5개의 영역이 생겼고, int 변수의 크기는 4바이트로, 9바이트 이상의 데이터를 입력시키면 프로그램을 크랙 할 수 있는 것이다. , int a 변수를 침범해서 터지지만, 인증성공을 띄워준다.



반응형

'~2019 > 해킹맛보기 스터디' 카테고리의 다른 글

2장. 웹 해킹  (1) 2016.10.11
1장. 맛보기(1단원)  (0) 2016.09.27
0장. 계획  (0) 2016.09.26
Comments