* 이 글은 SK Infosec의 오픈소스 소프트웨어 보안가이드를 참조하여 작성하였습니다.


Node.js Security


1. 데몬 관리


1.1 root 권한 실행 금지

  Node.js로 서버를 구동시켰을 때, root 권한으로 실행하면 안된다. root 권한으로 구동된 서버의 경우 취약점으로 인해 Exploit 당해 실행 권한을 넘겨주었을 때 그 권한이 root일 경우 상황이 심각해지기 때문이다.


1.2 요청을 전달할 HTTP 서버/proxy 형태로 구성

  Apache, nginx 등과 같은 HTTP 서버를 통해 요청을 받아 전달하도록 구성해야한다.


1.3 NODE_ENV 설정을 production으로 설정

  개발 당시 develpoment로 설정하여 개발 할 수 있는데, 서비스를 시작할 때 이를 깜빡하여 그대로 서비스를 구동하는 경우가 있다. 이럴 경우 에러에 관해 상세한 정보가 출력되기 때문에 이를 이용해 공격자들이 활용할 수 있다.


* 진단 방법

# ps -ef | grep node





2. 로그 디렉토리/파일 권한 설정


  로그 파일에 공격자에게 유용한 정보가 들어있다. 그래서 권한관리가 필요하며 일반 사용자들이 접근하지 못하도록 권한을 설정한다.


2.1 권한 설정

 # chown nodeapp:node /[구동중인 Node 어플리케이션 로그 디렉토리]    

# chmod 750 /[ 구동중인 Node 어플리케이션 로그 디렉토리]                

# chown nodeapp:node /[구동중인 Node 어플리케이션 로그 디렉토리]/*   

# chmod 640 /[ 구동중인 Node 어플리케이션 로그 디렉토리]/*  


  권한은 WAS 서버 계정 소유여야하며, 디렉토리는 750(drwxr-x---) 파일은 640(-rw-r-----) 권한으로 설정해야한다.


* 진단 방법

# ls –lad /[Node 어플리케이션 로그 디렉토리] 

# ls –la /[Node 어플리케이션 로그 디렉토리]/*






3. 로그 포맷 설정


  로그 포맷을 설정하지 않으면 침해사고 발생 시 공격 여부 파악, 공격자 사용 툴 파악, 공격자 위치 파악이 불가능하다. 그러므로 필요한 정보들로 로그 포맷을 설정해두어야한다.


* Node의 기본 console.log 또는 Express 4.x 이전 버전으로 지원 가능한 로그 출력 기능 외 morgan 등의 추가 로거 모듈을 사용하는 경우 해당되며, 파일의 형태가 아닌 콘솔 출력을 의미함. 어플리케이션 코드 내에서 제어하는 형태로 사용.


3.1 모든 요청에 대해 combined 포맷의 로그를 출력하도록 설정


var express = require('express') 

var morgan = require('morgan')   


var app = express()   


app.use(morgan('combined'))   


app.get('/', function (req, res) {   

   res.send('hello, world!') 

})


* 로그 포맷 지시자


combined : 표준 Apache combined 로그 출력 

:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"


common : 표준 Apache common 로그 출력

:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]


dev : 개발을 위해 response에 따라 색상이 입혀진 축약 로그 출력 

:method :url :status :response-time ms - :res[content-length]


short : 기본 설정보다 짧은 로그 출력, 응답 시간 포함 

:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms


tiny : 최소화된 로그 출력 

:method :url :status :res[content-length] - :response-time ms


* 사전에 morgan 모듈을 설치했다고 가정했다.


* 진단 방법

  소스 코드 내 log 내용 확인 또는 개발자 인터뷰

  로그포맷 설정 값이 combined가 아니거나 그에 준하지 않는 포맷 토큰 조함으로 설정되어 있는 경우 취약함.





4. 로그 저장 주기


  ‘정보통신망이용촉진및정보보호등에관한법률’, ‘개인정보보호법’, ‘회사사규’ 등에 따라 로그 파일은 최소 6개월 이상의 기간은 보관해야한다. 또한 담당자는 로그 기록을 정기적으로 백업 및 확인 감독!


 4.1 정보 보호 관련 법 및 개인정보보호법 등에 따라 최소 아래 기간 이상은 보관해야한다.


1) 사용자접속 기록

사용자 로그인/로그아웃/정보변경 등 -> 6개월 이상


2) 개인정보취급자의개인정보처리시스템접속기록

정보주체 식별정보/개인정보취급자 식별정보/ 접속일시/접속지 정보/ 부여된 권한 유형에 따른 수행업무 등 -> 2년 이상


3) 개인정보취급자권한변경기록 

개인정보취급자 권한 생성/변경/삭제 등 -> 5년 이상


4.2 담당자는 접속 기록을 월 1회 이상 정기적으로 확인·감독하여, 접속과 관련 된 오류 및  부정행위가 발생하거나 예상 되는 경우 즉각적인 보고 조치가 되도록 해야 한다.


4.3 접속 기록이 위·변조 되지 않도록 별도의 물리적인 저장 장치에 보관하여야 하며  정기적인 백업을 수행 해야 한다.


* 진단 방법

  서버 운영 또는 담장자 인터뷰






5. 헤더 노출 정보 방지


  HTTP 요청에 대한 응답 시 헤더에 서버 이름, 버전 등의 정보가 포함되어 공격자가 해당 정보를 공격에 이용할 수 있다.


* Node의 헤더 관련 개별 설정 방법 외 helmet과 보안 관련 HTTP 헤더 모듈을 사용한 경우엥 해당된다.


5.1 Node 어플리케이션 코드 내에 모듈 사용 설정 

var express = require('express');   

var helmet = require('helmet'); 

 

var app = express(); 

 

app.use(helmet()); 


  위 설정은 Apache, nginx 등의 웹 서버 환경 설정을 통해서도 같은 설정이 가능하다.


* 진단 방법

  소스 코드 내 헤더 설정 관련 확인 또는 개발자 인터뷰





* 보안 패치


  최신의 보안 패치 필요. Node.js 뿐 아니라 추가한 Express 모듈 또한 패치에 신경 써야한다. 주기적으로 보안 패치를 적용하지 않으면 exploit 공격, 제로데이 공격 등의 서버 침해가 발생할 수 있다.


* Node.js 0.10.42 이전 0.10.x, 0.12.10 이전 0.12.x, 4.3.0 이전 4.x, 5.6.0 이전 5.x 버전들에서 원격 공격자의 조작된 Content-Length HTTP 헤더를 통한 HTTP request smuggling 공격을 허용할 수 있다.


(* HTTP Request Smuggling 공격은 Web Hacking 카테고리에 글을 포스팅 하였다. ) 


HTTP Request Smuggling


Normaltic 작성

 

HTTP Request Smuggling (HRS) ?

HTTPContent-Length 헤더 변수를 조작하여 원하는 데이터 혹은 패킷을 Smuggling(밀수) 몰래 들여보내는 것이다. 이 공격을 통해 웹 서버 앞의 방화벽(F/W)을 우회하거나, 웹 캐시 서버의 Cache-Poisoning 등을 할 수 있다.

 

* 본 자료는 http://www.cgisecurity.com/lib/HTTP-Request-Smuggling.pdf 을 참조하여 작성하였다.


(1) Web Cache Poisoning

  이 공격을 가능하게 하는 취약점은 바로 HTTPPost 패킷에 Content-Length 헤더가 2개가 들어있을 때 받아들이는 방식이 웹 캐시 서버(Proxy)와 웹 서버(W/S)가 다르다는 점이다.

 

-> 그렇다면 HTTP Post 패킷에 Content-Length 헤더가 다른 값으로 2개 들어가있으면 서버는 어떻게 처리할까?

이는 서버마다 다 다르다. 예를 들면, SunONE W/S 6.1 (SP1)의 경우 첫 번째의 Content-Length 헤더를 사용한다. 반대로 SunONE Proxy 3.6 (SP4)의 경우 두 번째의 Content-Length 헤더를 사용한다.

 

위와 같은 차이로 인해 악의적인 Web Cache Poisoning이 가능하다. SunONE W/S 6.1 (SP1)의 서버 앞에 SunONE Proxy 3.6 (SP4)Proxy 서버로 있으며 SITE라는 DNS 주소를 가진 서버라고 가정하고 공격해보겠다.


< 공격 모습 >


  위와 같은 패킷들이 서버에 전달 되었을 때, Proxy 서버의 경우 1-7 번 라인을 POST Request라고 먼저 인식하게 된다. 그리고 Content-Length 헤더가 두 개 있다는 것을 알게 되는데 여기서 Proxy서버의 경우 첫 번째 Content-Length 헤더를 무시하고 두 번째의 Content-Length 헤더를 사용하여 Body의 길이가 44라고 인식하게 된다. 여기서 8-10 번 라인이 정확하게 44 바이트이다. 그러므로 8-10 번 라인을 첫 번째 POST 패킷의 Body라고 인식하게 되는 것이다. 그 다음으로 Proxy 서버는 11-14 번 라인을 GET Request로 인식한다.

 

  이제 웹서버의 경우를 보자. 웹 서버의 경우 Proxy 서버와 달리 첫 번째 Content-Length 헤더를 사용한다. 그래서 Body 길이가 0이라고 인식한다. 그렇게 되면 8번부터 새로운 GET 요청으로 인식하게 되는 것이다. 여기서 주목할 점은 10번 라인 다음에 CRLF 이 없다는 것이다. 그렇기 때문에 11번 라인이 새로운 GET Request로 인식되지 않고 8번 라인의 GET Request 패킷의 Bla 헤더의 으로 인식 되는 것이다.

 

  정리해보면 다음 표와 같다.


< 정리 >


  ProxyWeb 서버에서 둘 모두 2개의 Request가 왔다고 인식한다. 하지만 그 2개의 내용이 다르다. 어떤 2개의 Request인지는 위의 표로 정리하였고 내용은 다음과 같다.

 

  Proxy에서 2번 째 Request“http://SITE/page to poison.html”을 요청한다고 인식하였고, Web 서버에서 2번째 Request“/poison.html”을 요청한다고 인식하였다. Web 서버는 “/poison.html” 요청의 결과를 돌려주는데 이 때 Proxy 서버가 이를 캐싱해 둔다면 Proxy 서버는 “http://SITE/page to poison.html” 요청과 “/poison.html”의 결과를 매칭시켜 저장하게 된다.

 

* 더 강력하게 효과 보는 경우

  만약 서버가 가상 호스팅하고 있는 경우라면, 효과가 더 강력할 수 있다. IP는 같으므로 HTTP 패킷 안에 host 헤더만 다시 설정하므로써 다른 많은 사이트들에 대해 공격을 할 수 있다.

 

  이를 통해 악의적으로 Web Cache를 조작할 수 있게 된다. 이 것이 Web Cache Poisoning의 공격에 활용 될 수 있는 방법이다.


(2) Firewall/IPS/IDS 우회

 

방화벽이나 IPS, IDS의 경우 특징 기반으로 악성 패킷을 검출한다. 예를 들어 “cmd.exe”라는 문자열이 URL에 들어가 있다거나 하는 특징들이 있다. 혹은 SQL Injection에서 사용하는 특징들을 생각해 볼 수 있다.

 

이 자료에서 보여주는 공격은 IIS/5.0 기반 서버에서 이루어진다. ISS/5.0는 큰 Body를 가진 POST Request를 처리할 때 버그가 일어난다. 바로, RequestContent-Type헤더에 정해진 타입이 아닌 경우 49,152 Bytes(48K) 만큼 잘라서 읽는다. 그래서 48K + X 의 크기로 보내게 되면 X 만큼의 데이터를 Smuggling할 수 있게 된다.

 

아래 공격 예시는 ISS/5.0 서버이고 앞 단에 Firewall이 설치 되어있는 경우이다. 공격 목적은 Firewall을 우회하여 서버에 “cmd.exe” 문자열이 도착하게 하는 것이다.


< 공격 패킷 >


  위와 같은 모습으로 패킷을 전달하게 되면, Firewall 입장에서는 첫 번째 Request49223 Byte 크기의 Body를 가지고 있다고 인식하게 된다. 위에서 보면 10번 라인 까지를 첫 번째 Request로 인식한다. 그 다음 Firewall은 두 번째 Request11번 라인부터 인식하게 되는데 주목할 점은 12번 라인이다. 12번 라인 뒤에 CRLF가 없다. 그렇기 때문에 13번라인부터가 두 번째 RequestBla헤더의 값에 포함되어 인식되는 것이다. 그렇게 되면 Firewall은 이 2가지 패킷을 막지 않는다. 이유는 URL에 공격 패턴이 없었기 때문이다.

 

  하지만 Firewall을 지나서 서버에 도착하면 상황이 달라진다. IIS/5.0 서버에서 첫 번째 Request를 보면 Content-Type이 없다. 그렇기 때문에 서버는 49152 크기로 Body를 잘라버린다. 그러면 1-6번 라인이 첫 번째 Request로 인식 되는 것이다. 그리고 두 번째 Request7번 라인부터 인식이 되는데, Content-Length30으로 되어있다. 그래서 Body11-12번 라인이 포함된다. 그렇게 되면 서버 입장에서는 다음 Request13번부터 받아들이게 되는데 13-15번 라인의 Request를 보면 URL“cmd.exe” 문자열이 있는 것을 확인 할 수 있다. 그렇게 되면 서버 입장에서는 “cmd.exe”를 실행되어 공격이 이루어 질 수 있다. 인식하는 Request를 정리해보면 아래 표와 같다.


< 정리 >


(3) Forward VS Backward HRS

 

일반적으로 HRS 공격은 3개의 Request를 보내는데, Content-Length를 조작하여 각각 다른 2개의 Request로 보이게 하는 원리로 공격이 일어난다. 첫 번째 예에서는 req1, req2를 웹 서버가 인식하고, req1, req3Proxy가 인식하므로써 공격이 일어났는데, 이러한 경우를 Forward Smuggling이라고 한다.


< Forward Smuggling >


  이와 반대로 Backward Smuggling이 있다.

< Backward Smuggling >


  Forward Smuggling보다 Backward Smuggling 공격이 훨씬 어렵다. 그 이유는 ProxyRequest를 받고 웹 서버에 전달하는데 그에 대한 응답을 받을 때 까지 다음 Request를 보내지 않기 때문이다. 이럴 경우 Proxy는 첫 번째 Request라고 인식하여 보내고 그에 대한 응답을 기다리는데 웹 서버의 경우 Request가 다 도착하지 않았으므로 기다리게 된다. 그러면 Deadlock에 빠지게 된다. 이렇게 보면 이론적으로 Backward Smuggling이 불가능한 것처럼 보인다. 하지만 예외의 경우가 있다.


  이번 예시는 DeleGate/8.9.2 캐시 서버를 사용한다. 그리고 웹 서버로는 IIS/6.0, Tomcat, SunONE 서버 등을 사용할 수 있다. 이번 트릭은 바로 이 것이다. 우리는 GET Request를 사용할 것이다. 그런데 이 GET RequestContent-Length 헤더와 값을 넣어서 보낼 것이다. DeleGateGET RequestContent-Length0이라고 생각한다. , Body가 없다고 생각하는 것이다. (실제로 GET에는 Body가 없다.) 반대로 웹 서버는 Content-Length 값 만큼의 Body가 있는 Request라고 생각한다. 그렇지만 웹 서버는 Body를 받기 전에 응답을 보내준다. 바로 이 점에서 Backward Smuggling이 가능한 것이다. 웹 서버는 Body가 있는 Request라고 생각하지만, GET 요청이므로 Body부분이 도착하기 전에 응답을 보내는 것이다.

 

  공격 예시는 다음과 같다.


< Backward Smuggling >


  위의 패킷을 보내게 되면, DeleGateContent-Length 헤더를 무시한다. 이유는 GET 요청이기 때문이다. 첫 번째 요청을 1-6 번 라인이라고 인식한다. 그 후 7-10번 라인 까지를 두 번째 요청이라고 생각한다. 이유는 8번 라인 다음에 CRLF가 나오지 않았기에 9-10번 라인이 Bla 헤더의 값이라고 생각하기 때문이다. 이와 반대로 웹 서버에서는 첫 번째 요청이 40 바이트 크기의 Body를 가진 요청이라고 생각한다. 그렇기에 1-6번 라인까지 받았을 때 GET이기 때문에 응답은 하되, 나머지 40 바이트 크기의 Body를 기다린다. 그 후 7-8번 라인까지 40 바이트가 들어오면 첫 번째 요청의 Body라고 인식한다. 그 후 9-10번 라인의 요청을 두 번째 요청이라고 생각하는 것이다. 이렇게 악의적으로 인식되는 요청은 3번째(후자) 이므로 Backward Smuggling이 가능하게 되는 것이다


(4) Request Hijacking ( HTTP Request Smuggling Through a Proxy Server )


  HTTP Request Smuggling 공격을 통해 XXS 공격과 비슷한 결과를 만들 수 있다. XXS 공격이란 간단히 설명하면 클라이언트(피해자) 컴퓨터에서 악성 스크립트가 실행되도록 하는 공격이다. 비슷한 결과를 만들지만 XXS 공격보다 훨씬 더 성능이 좋다. 그 이유는 HttpOnly Cookie 정보나 HTTP 인증 정보들을 직접 가져갈 수 있기 때문이다. ( Cross Site Tracing이 필요없다. )

 

(* 참고 : Cross Site Tracing : HTTPTRACE 방식을 지원 할 때 가능. )

 

  Request Hijacking의 선행 조건

 

    1. 서버 앞단에 Proxy 서버가 존재해야한다. ( Web Cache Poisoning과 달리 캐싱기능은 없어도 된다. )

    2. 해당 웹 서버에 XXS 취약점이 존재해야한다.


< Request Hijacking 원리 >


  공격 예시는 위와 같다. 위 데이터를 보내게 되면, Proxy 서버에서는 두 번째 Content-Length로 인식하고 위 전체 내용이 하나의 요청으로 전달된다. 하지만 서버에 도착해서 서버가 해석하기를 첫 번째 Content-Length로 인식하여 1-7번 라인(this=that까지) 의 요청으로 인식하고 응답한다. 그 뒤 데이터를 POST 요청으로 인식한다. 여기서 Content-Length95로 잡아주었는데 여기서 94 바이트만 제공되어있다. 그러면 나머지 한 바이트를 서버는 기다리게 된다. 이 때 피해자가 GET 방식으로 요청을 했다고 가정하자. 그렇게 되면 한 바이트를 기다리면 요청이 있는데 여기에 데이터의 맨 앞 ‘G’라는 문자가 소모된다


< 완성되는 요청 >


 

  그렇게 되면 위와 같은 요청이 완성된다. 이 요청에 대한 응답으로 cookie를 출력하는 스크립트가 피해자에게 전송되는 것이고 그렇게 피해자의 컴퓨터에서 임의의 스크립트가 실행되는 것이다.

 

  그런데, 이는 HTTP Request Smuggling을 통해 어떻게 악성 스크립트가 실행되는지를 확인한 것이다. 이를 이용해 세션정보가 있는 cookie와 인증 정보를 탈취하기 위해서는 약간의 트릭이 필요하다.


< 자바 스크립트 >


  위 자바 스크립트는 document의 텍스트 정보들을 단일 문자열로 concat한 후, 앞에서부터 300바이트를 잘라내는 동작을 한다. 이를 이용해 다음과 같은 공격 패킷들을 구성했다고 해보자.


< 스크립트를 추가한 패킷모습 >


  param1 변수부터 시작되는 body의 길이는 277바이트만 제공하였다. 이렇게 되면 나머지 300바이트를 웹 서버는 기다리게 된다. 피해자가 보낸 요청으로부터의 300바이트를 사용하게 된다. 이 상태의 응답이 클라이언트(피해자)에게 전송되고 피해자의 브라우저에서 해당 응답이 로딩될 때, 삽입한 스크립트가 실행된다. document의 텍스트 부분 앞에서 300바이트를 잘라내는데 이 안에 보통 Cookie와 인증 정보 헤더가 담겨있다. 또한 이와 함께 URL도 담겨있는데 이를 활용해 해당 사이트의 인증 정보와 쿠키를 Hijacking 할 수 있다.


(5) Request Credential Hijacking ( HTTP Request Smuggling Through a Proxy Server )

 

  클라이언트의 크리덴셜을 가지고 특정 스크립트를 실행시키는 또 다른 방법이 있다. 이 방법은 클라이언트의 크리덴셜을 가지고 악성 스크립트를 실행한다는 것으로 CSRF(Cross-Site Request Forgery) 공격과 비슷하다. 그러나 CSRF보다 훨씬더 효과가 좋다. 그 이유는 클라이언트(피해자)와 상호작용할 필요가 없기 때문이다.


< 공격 패킷 >


  이 때 피해자가 아래와 같은 요청을 했다고 가정하자.

< 피해자의 요청 >


  그렇게 되면 위 공격 패킷의 빨간 부분(GET 요청)에 최종적으로 아래와 같은 패킷이 만들어지게 된다

만들어진 위조된 요청 >

이 요청은 ‘/some_page.jsp’의 페이지에서 피해자의 크리덴셜을 가지고 요청이 되는 것이다. 그렇게 되면 CSRF와 비슷한 효과를 낼 수 있다. 만약 이 ‘/some_page.jsp’ 페이지가 비밀번호 변경 페이지라던가, 큰 돈을 전송하는 페이지였다면 피해가 컸을 것이다.


오늘은 CoffeeNet 프로젝트를 Github에 올렸다.

나는 블로그 처럼 뚝 하고 올리는 건줄 알았는데, 아니었다.
Git  이라는 Source Code Management 개념이있었다.

프로그램을 만들 때 버전 관리를 위해서, 그리고 다른 사람들과 같이 작업하기 위해
git을 사용한다고 한다.
local git에 저장하고 있다가, remote git에 저장하고 가져오고 한다.
오픈소스라 원격에 저장한 것은 다른 사람들도 쉽게 가져가서 수정하고 할 수 있다.

그 remote git 저장소 중 하나가 github이다. 제일 잘 나가는 저장소 중 하나이다.

github에서 계정을 만들고 새 저장소를 만든다.



(저장소 생성)



생성된 저장소는 아래와 같다.




(생성된 저장소)


이제 remote 저장소와 현재 저장소를 연결해야한다.



(사용자 정보 입력)


사용자 정보를 입력한다.

그리고 remote 저장소와 연결



(remote 저장소와 연결)


이와 같이 하면 연결은 끝난다.

이제 현재 내 VM-kali 호스트와 계정을 연결해 둘 것이다.



(SSH 연결)


ssh 공개키를 내 계정에 저장해 두었다.

기존에 있는 프로젝트와 연결하고 push 하려했더니 자꾸 싱크가 안맞아서 에러가 났다...

그래서 할 수 없이 새로 다른 폴더를 만들고, 거기서 git clone을 해준 뒤, 작업했다.


(add 전)


git clone 한 폴더에 내가 작업했던 파일들을 다 복사했다.

그리고 git status를 치니 아직 추적되지 않은 파일들이 나온다.


(add)


해당 폴더의 모든 파일을 add 해준다.
이렇게 되면 index에 다 들어가게 되고

commit을 해준다.


(commit)


이제 로컬 저장소에 변경 사항들이 저장되었다.

이제 이 정보들을 remote 저장소에 push 해주면 된다.


(push)


이렇게 다 하면 내 CoffeeNet 저장소에 아래와 같이 내가 그 동안

작업했던 내용들이 업로드 된다.



(원격 저장소)


이제 CoffeeNet 프로젝트를 소개할 README.md 를 작성해야한다.



(README.md)


위와 같이 간단히 작성하였다.

방금 작성한 README파일을 내 로컬에도 옮겼다.



(pull)


pull, push를 이용하여 github에 소스코드를 올리고 내려받으면서 관리할 수 있다.

추가적으로 이제 github를 통해 다른 사람들이 나의 코드를 받아갈 수 있다.
그런데 바로 실행이 안될 가능성이 크다.
python3 가 없을 수도있고, 내가 사용한 모듈이 없을 수도 있기 때문이다.

그렇기에 이와 관련되어 필요한 파일들을 설치해주는 setup.sh 쉘스크립트를 작성했다.



(setup.sh)


setup.sh 은
처음 작성해보는 쉘스크립트였는데,
Veil-Framework의 setup.sh를 참고하여 공부하면서 작성하였다.

그리고 파이썬의 필요한 모듈들을 파일로 이름들을 저장해두었다.


(필요한 모듈)


이 파일을 읽어서
하나씩 있는지 검사하고 없으면 설치해주는 쉘스크립트이다.

이로써, github에 업로드 까지 끝났다!!

CoffeeNet 프로젝트의 한 축이 끝났다.
이제 이 버전에 새로운 Tool을 추가하면서, 기존에 있던 코드들을 보수하고 수정하면서 관리할 계획이다.


지난 프로젝트에서의 계획
1. custom 메뉴 기능 구현 (커스텀 옵션 설정기능)
2. Framework의 update 기능 구현
3. 그 외 마무리 작업.

오늘 대망의 이 스캐너 작업이 끝났다.!!!
마지막으로 생각하던 custom 으로 옵션을 설정하는 기능을 추가하였고,
그 외에 마지막으로 스캐너의 기타 기능들을 정리하고 마무리하였다.

custom 옵션같은 경우, 스캐너 클래스에 custom_option이라는 문자열 변수를 놓고 여기에 옵션을 사용자로부터 입력받아 저장하고, 스캔할 때 이 옵션을 뒤에 추가한다.


(추가한 옵션 변수)


여기에 옵션을 사용자로부터 입력받아 저장한다.



(실행 코드)


실행시 이 변수를 옵션 뒤에 붙여서 실행해준다.



(실행 모습)


옵션을 입력받는 모습이다.
여기서 경고문구로 정확히 잘 모르면, 사용하지 말라 라는 경고문을 넣었다.

왜냐하면 부정확한 옵션을 입력으로 넣게 되면 스캔 작업때 에러가 나기 때문이다.


(입력된 옵션)


입력을 받게 되면 스캔 메뉴에서 직접 어떤 옵션이 들어가있는지 확인 할 수 있다.


(실행 모습)


실행시 매우 잘 동작하였다.

아무리 경고를 해놨다 해도, 이에 대해 잘 처리를 안해두면 훌륭한 프로그래머가 아니다.


(에러 모습)


분명 이런 에러가 나올 것이다.

이를 막기 위해 예외 처리를 하였다.



(예외 처리)


예외처리를 하였다. 총 스캔하는 코드가 3부분 있었는데 그 곳에 이렇게 예외처리를 하였다.


(예외처리 모습)


부정확한 옵션으로 인한 에러가 나게 되면

에러를 출력한다. 그리고 현재 입력된 옵션을 지워주게 만들었다.



(옵션 삭제)


옵션이 삭제된 모습이다.

마지막으로 update 기능을 구현하려 했으나,

깃헙에 올려서 배포를 한 후, 깃헙을 이용해 업데이트 작업을 진행할 계획이어서 여기서는 구현하지 않았다.


(update 미 구현)


그래서 이 버전에서는 업데이트 명령이 지원되지 않는다는 문구를 남겨주었다.

이로써 스캐너 작업을 마무리 함으로써
커피넷이라는 프레임워크에서 스캐너가 완성이 되었다.
아직 도구가 하나 밖에 없지만, 커피넷이라는 프레임워크가 완성됬다.

이제 남은 작업은 깃헙 페이지에 올려서 배포하기 위해
README 이런것들을 작성할 예정이다.

오늘 구현한 기능
- custom 메뉴 기능 구현 (커스텀 옵션 설정기능)
- 그 외 마무리 작업 (스캐너 완성)

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.
4. scanner의 host scan 기능
5. scanner의 detail scan default기능
6. detail 결과 전체 출력기능(ip, mac, vendor ,os)
7. db 메뉴 생성 및 scanner의 오픈 포트 출력 기능
8. detail 스캔의 예외모드 기능 구현
9. nmap의 호스트 스캔 성능 고민.
10. nmap의 속도 개선 고민.
11. custom 메뉴 기능 구현 (커스텀 옵션 설정기능)


다음 계획
1. github 페이지 업로드 준비 및 업로드


지난 프로젝트에서의 계획
1. detail 스캔의 예외모드 기능 구현
2. Framework의 update 기능 구현
3. nmap의 호스트 스캔 성능 고민.
4. nmap의 속도 개선 고민.

-> 오늘 구현한 것은 먼저 detail 스캔의 예외모드 기능을 구현해보았다.
그리고 nmap의 스캔이 오락가락했던 것에 대해 고민해보고,
nmap의 속도를 개선시키기 위해 고민해보았다.

그리고 추가적으로 메뉴를 조금씩 바꾸고 있다. 아래는 스캐너 모듈의 스캔 메뉴를 조금 바꾸었다.



(스캔 메뉴)


스캔 메뉴에서 Target IP 라는 것을 Target Network라고 바꾸었다. 타겟 설정이 불필요해 보여서 현재 공격 대상의 네트워크 대역을 표시해 주는 것으로 하기로 했다. 그리고 타겟 설정 메뉴를 없앴다. 단순히 detail 뒤에 인자로 아이피 주소를 주면 해당 대역을 스캔해 주므로, 불필요하다고 생각했다.
그리고 추가적으로 nmap의 옵션을 사용자 입맛에 맞게 옵션을 줄 수 있도록 custom 메뉴를 만들었다.
이 메뉴를 통해서 사용자는 자기 입맛대로 nmap의 옵션을 설정할 수 있다.

그리고 detail 스캔의 예외 모드이다.
예외 모드란 host 스캔 없이 바로 detail 스캔메뉴로 들어온 경우이다.

이럴 경우 기존 host 데이터가 없으므로 detail 스캔을 진행하지 못하므로, ip 주소를 입력받아 해당 ip 주소를 detail 스캔 하는 방식으로 작성하였다.


(detail 스캔 예외모드)


실행하면 다음과 같이 잘 동작하는 것을 확인하였다.


(동작 확인)


그리고 추가적으로,
detail 뒤에 ip 주소를 인자로 던저주면 해당 ip에 대해 detail 스캔을 해주는 기능을 추가했다.

코드에서는 두번째 인자를 ip 형식이 맞는지 체크하는 루틴을 위에서 만든것과 같이 똑같이 만들었다.



(동작 확인)


동작이 잘 되는 것 확인!
위에 두개가 뜬 것은 제일 먼저 192.168.0.1 에 대한 정보가 있는 상태에서
추가적으로 192.168.0.4 를 스캔하여 192.168.0.4의 정보가 추가 된 상태로 보여주기 때문이다.

그 다음 고민한 것이 스캔 속도였다.
호스트 스캔의 경우 여러 대의 호스트여도 빠르게 스캔이 가능하였다.
하지만 detail 스캔의 경우 20대 정도만 되도, 기다리는게 한 세월이었다.

그래서 찾아본 결과 -T 옵션이 아래와 같은 기준으로 세부 옵션을 설정해주는 거였다.


(-T 옵션)


여기서 나는 디테일 스캔에서 -T5 옵션을 사용하기로 결정했다.
-T5 의 경우 빠르긴하지만, 단점이라면 트래픽이 많고 시끄럽다는 것이다.

그런데 카페 같은 소규모 네트워크에서 사용할 프레임워크로써 그러한 소음에는 덜 민감해도 된다고 판단하였고, 카페같은 무선랜을 주로 사용하는 곳에서는 호스트가 유동적으로 연결 되었다 해제된다. 그러기에 빠르게 스캔할 줄 아는게 굉장히 중요하다고 판단하여 -T5 옵션을 사용하기로 결정했다.


(-T 옵션 장착)


그 외로 이제 정상적인 스캐너의 기능을 할 줄 알게 되는 스캐너를 구현하였다.



(스캐너 구현)


마지막으로, 스캐너가 오락가락하는 것에 대해 고민해보았다.
내가 실험했던 곳은 무선랜이 대다수였는데, 안되는 곳의 특징을 살펴보니, 주파수 간섭이 심한 곳이었다. 이런 곳에서는 --scan-delay를 조금 길게 설정해주니, 안잡히던 호스트도 잡히고 하는 현상을 발견했다. --scan-delay는 스캔하고 기다리는 시간을 주는 것인데, 주파수 간섭이 심한 곳 같은 경우에는 패킷이 전송실패할 경우가 많기 때문에 이 시간을 조금 늘려주는 것이 안전하였다.

-> 이런 경우 custom 옵션을 사용하여 추가해주거나, 이러한 경우를 위한 메뉴를 하나 더 설정할 생각이다.

오늘 구현한 기능
- detail 스캔의 예외모드 기능 구현
- nmap의 호스트 스캔 성능 고민.
nmap의 속도 개선 고민.

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.
4. scanner의 host scan 기능
5. scanner의 detail scan default기능
6. detail 결과 전체 출력기능(ip, mac, vendor ,os)
7. db 메뉴 생성 및 scanner의 오픈 포트 출력 기능
8. detail 스캔의 예외모드 기능 구현
9. nmap의 호스트 스캔 성능 고민.
10. nmap의 속도 개선 고민.

다음 계획
1. custom 메뉴 기능 구현 (커스텀 옵션 설정기능)
2. Framework의 update 기능 구현
3. 그 외 마무리 작업.


지난 프로젝트에서의 계획
1. 오픈포트 정보, 서비스를 확인할 수 있는 기능 추가
2. detail 스캔의 예외모드 기능 구현
3. Framework의 update 기능 구현

이 중오늘 구현한 것은 detail 스캔의 오픈된 서비스 정보를 출력하는 것을 구현하였다.
이를 위해 어떠한 인터페이스가 좋을지 고민을 해보았는데,
db 라는 메뉴를 하나 더 만들어서

db 메뉴에서 지금까지 스캔한 정보에 대한 정보를 선택해서 볼 수 있는 메뉴를 만드는게 좋을 것 같았다.



(추가한 db메뉴)


db 메뉴에 들어가면 아래와 같다.


(db 메뉴)


db 메뉴에 들어가면 현재 스캔된 호스트 정보의 개수를 알려준다. 현재 7개가 들어있다.

- host 메뉴에서는 호스트 리스트를 출력해준다.
- service 메뉴에서는 오픈된 포트를 출력해준다.
- back 메뉴는 Scanner 메뉴로 돌아간다.
- help 메뉴는 db 메뉴에 관한 help를 만들었다. 이유는 db에서 사용하는 명령어에 대해 알려주는 게 있어야할 거 같아서 메뉴만 만들어두었다.

host 리스트 정보 출력



(호스트 리스트)


여기서 service [ip] 이렇게 입력하면

입력된 ip에 열려있는 포트 정보를 출력해준다.



(open 된 port 정보)


예외적으로 호스트된 정보가 없다면 데이터가 없다고 출력해준다.



(호스트 정보 없음)


service 메뉴에 관한 코드는 아래와 같다.



(service 메뉴)


현재 1개의 IP 정보가 추가적으로 들어와서 그 IP정보에 대한 오픈 포트의 정보를 출력하게 만들었다.
아무것도 없이 service만 입력을 하면 호스트 전체의 service를 출력하도록 하는 코드로 만들 것이다.

오늘 이어서 만들다 보니 또 문제점을 발견하였다.
-> nmap이 오락가락? 한다.
호스트가 잡힐 때가 있고 안잡힐 때가 있다. 기본적으로 가장 중요한 Gateway도 잡혔다 안잡혔다 한다...
이유는 잘 모르겠다. 어떤 때는 Gateway까지 전부 완벽하게 스캔되고,
어쩔 때는 잘 안된다.

wireshark로 분석을 해본 결과 nmap은 ip대역을 넣어주면, 처음 arp 스캔을 한다.
그리고 arp 스캔 결과를 가지고 추가적인 작업을 하는 것을 확인했다.
분명 gateway가 arp 응답을 했음에도 불구하고, host-down이라고 뜰 때가 있었다.

ping gateway는 너무너무 잘나간다.
그리고 바로 이어서 핑스캔으로 현재 로컬 ip대역으로 설정한 후 실행하면 게이트웨이가 안잡힐 때가 있다.
(?!)
정리하면
nmap -sP -sn 172.30.1.0/24   ->  gateway 안잡힘.
nmap -sP -sn 172.30.1.254    -> 254가 게이트웨이인데, 이렇게 하면 잡힘.

하아.. 성능을 높이기 위해
ping 스캔을 직접 구현해야하나 고민 중이다.
추가적으로 detail 스캔할 때 느리다는 단점이 있었다.
물론, 카페같은 소규모 네트워크를 위한 프레임 워크이지만, 호스트 대수가 20대가 넘어가면
세월아 내월아 함흥차사다.
적어도 50대까지는 나름 그래도 잘 커버할 수 있게 만들어야 될거 같다.
지금 생각으로는 thread를 이용하여 스캔방식을 달리할 생각이다.
nmap 자체에서 thread를 이용하는거 같았는데, 이 또한 자세히 조사해서 알아봐야할 듯 싶다.

오늘 구현한 것에 비해 해야할 일들이 더 늘은 느낌이다...!
(아! detail scan을 출력하는 기능은 다른 곳에다 엮을 생각이다.)

오늘 구현한 기능
- scanner의 오픈 포트 출력기능
- db 메뉴를 새로 만듬.

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.
4. scanner의 host scan 기능
5. scanner의 detail scan default기능
6. detail 결과 전체 출력기능(ip, mac, vendor ,os)
7. db 메뉴 생성 및 scanner의 오픈 포트 출력 기능

다음 계획
1. detail 스캔의 예외모드 기능 구현
2. Framework의 update 기능 구현
3. nmap의 호스트 스캔 성능 고민.

4. nmap의 속도 개선 고민.


Rootkit의 원리를 이해하기위해
간단한 Rootkit을 만들었다.

특수 코드인 1018을 입력으로 넣으면 root 권한을 넘겨주는 rootkit을 만들것이다.


(Prototype)


모듈은 디바이스로 관리가 된다. 디바이스는 리눅스에서 파일처럼 다뤄진다.
디바이스와 입출력을 하기위해 open, read, write 함수를 정의해준다.
open과 read는 여기서 사용하지 않을 것이므로 모양만 만들어준다.

여기서 write함수를 채워준다.


(write 함수)


write함수를 보면 유저 영역의 입력을 받는다.
입력을 받아서 첫 4자리를 비교한다. 첫 4자리가 1018이라면
root권한인 id를 0으로 만들어준다.

Makefile을 만든다.


(Makefile)


그 후 insmod로 만든 모듈을 로딩한다.

로딩한 후, chmod를 이용하여 /dev 아래에 있는 해당 디바이스를 666으로 만들어준다.

그 이유는 루트 권한이 아닌 일반계정에서 실행할 수 있도록 하기 위해서이다.


(권한 설정)


실행하기 전 권한을 확인한 모습이다.


(권한)


일반 사용자 계정이다.

그 후 /dev 아래에 만들어진 normaltic_door 가 있다.

이 디바이스에 1018이라고 입력을 넣는다.


(매직값 입력)


그렇게 되면,

위에서 작성한 write에 의해서 사용자 권한을 0으로 만들어준다.


(root 권한 획득)


root 권한을 획득했다.

간단한 rootkit을 만들어보면서
rootkit의 원리를 이해할 수 있었다.

유저 영역이 아닌, 커널 영역에서 root권한으로 권한상승시킨다는 개념이 이렇게
커널 영역에서 이루어지는 것을 확인할 수 있었다.

* 참조

https://0x00sec.org/t/kernel-rootkits-getting-your-hands-dirty/1485

지난 프로젝트 일지에서의 계획
1. 호스트 리스트를 가지고 디테일 스캐닝 기능 구현
2. Framework의 update 기능 구현

이번에 구현한 것은 디테일 스캐닝의 기능을 구현했다.



(detail 명령)


추가한 코드 시작부분이다.


(코드)


detail 명령시 기존에 있던 hosts 리스트에 있던 ip들을 대상으로 detail하게 scan을 한다.
원래 순서는
host scan을 한 후 detail scan을 한다고 가정을 했기 때문에 default로는 host scan의 결과가 target ip가 된다.
그러나 host scan이 없어서 hosts리스트가 없다면 특정 IP를 입력 받을 수 있도록 만들었다.
이 때 정규식 표현을 통해 IP 형식인지를 체크한다.
* 아직 detail 스캔의 예외모드는 구현하지 않았다.

default 방식인 host 스캔을 했다는 가정을 한 디테일 스캔을 완성하였다.


(스캔 옵션)


디테일 스캔 옵션으로는 스텔스하게, 운영체제 탐색, 서비스버젼 탐색을 넣었다.
그리고 기존 hosts 사전에서 담겨있던 mac, vendor 정보를 저장해둔다.
마지막에 사전으로 다시 저장할 것이다.

운영체제, 오픈 포트 및 서비스를 분석하여 사전으로 저장한다.


(스캔 정보 저장)


이렇게 저장된 스캔 결과를 확인'해보았다.


(호스트에 저장된 정보)


이제 이 정보들을가지고 화면에 출력해주면 된다.

이번에 구현한 기능은 detailscan의 결과를 출력하는 함수를 만들었다.


(detailscan 출력)


아이피, 맥, 벤더, 운영체제  이렇게 4가지의 정보를 나열해서 보여주게 만들었다.


(구현 결과)


Detail Scan 기능을 구현하였다.

오픈 포트정보와 함께 서비스도 알아내었는데, 그 정보를 아직 출력하는 기능은 넣지 않았다.
추가적으로 다음에 오픈포트 정보도 확인할 수 있는 기능을 추가할 것이며, detail 스캔의 예외 모드를 구현할 것이다.

오늘 구현한 기능
- scanner의 detail scan default기능
- detail 결과 전체 출력기능(ip, mac, vendor ,os)

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.
4. scanner의 host scan 기능
5. scanner의 detail scan default기능
6. detail 결과 전체 출력기능(ip, mac, vendor ,os)

다음 계획
1. 오픈포트 정보, 서비스를 확인할 수 있는 기능 추가
2. detail 스캔의 예외모드 기능 구현

3. Framework의 update 기능 구현


최근 워게임을 풀다가 RootKit이라는 개념을 접하게 되었다.
RootKit...
이름은 많이 들어본것 같이 익숙한듯 낯설다.

그리하여, RootKit에 대해 알아보았다.

RootKit
- 특정 시스템을 해킹 한 후 시스템의 제어권을 획득할 목적으로 설치하는 악성 프로그램.
(Post Exploit에 해당한다.)

Rootkit의 성능 지표
1) 원하는 대로 제어권을 획득 할 수 있는지
2) 탐지 되지 않는지

이 조건을 높은 수준으로 충족시키기 위해서는 커널 모드에서 동작하는 RootKit을 작성하여야 한다.

...!
....!
RootKit을 한번 제작해보고 싶어졌다. (아주 간단한 옛날 버전의 리눅스더라도...!)
그래서 나의 프로젝트에 RootKit 제작이 추가되었다.

현재, 학교의 엄청난 과제양과 이미 벌려놓은 수많은 프로젝트들이 있지만,
그래도 정말 하고싶다.! 그래서 시작!!!

(성능좋은) RootKit을 제작하기 위해서는 커널 영역에서 놀 줄 알아야한다.

그러한 의미로 LKM을 공부해보았다.
LKM 입문? 정도다.

LKM은 Loadable Kernel Module 이다.

- 커널영역은 유저영역에서 직접 접근할 수 없다. 커널영역을 사용해야할 때는 system call을 이용하여 커널영역을 사용하게 된다.

- 커널 모듈은 커널에 올라가는 모듈이다. 운영체제 리눅스는 커널 모듈들의 모음이라고 할 수 있다. 다양한 커널모듈들이 있고 각각의 모듈들이 디바이스를 관리하는 역할을 한다. 우리는 LKM을 간단히 만들어 커널영역에 올리는 작업을 해볼 것이다.

그러기 위해 필요한 것이 있다. 바로 linux 헤더이다.


(linux-headers)


자기 운영체제 버전에 맞는 것을 설치해야한다.
먼저 search로 검색을 한 후,

알맞은 운영체제 버전을 선택해서 설치하면 된다.


(설치)


그렇게 되면 준비는 끝난다.

간단한 LKM 을 만들기 위해

hello.c 파일을 작성하였다.


(hello.c)


MODULE 로 시작한 코드들은 해당 모듈에 관한 정보를 적는 것이다.

그 아래 static으로 변수를 만들어 주었다.
주의할 점은 이러한 모듈을 만들 때 전역변수를 함부로 사용하면 안된다. 이유는 이 모듈은 커널에 올라가게 되는데, 전역변수를 사용하게 되면 이곳저곳의 프로세스에 영향을 줄 수 있기 때문이다. 그렇기에 static으로 하여 해당 모듈에서만 영향력있게 만든다. 전역변수를 썼다가 혹여나 다른 프로세스에서 겹치는 변수가 있게 되어 꼬여버릴 수 있기 때문이다.

그 아래 module_param 함수는 변수에 관한 설정을 해주는 것이다. 첫번째는 변수 이름이고, 두번째는 type이다. 여기서는 char pointer 이므로 charp 라고 적어준 것이다. 마지막은 접근 권한이다.

그 아래 init과 exit 함수가 있다. init 함수는 모듈이 올라갈 때 실행되며, exit 함수는 모듈이 해제될 때 실행되는 함수이다. 여기서는 printk를 이용하여 커널 로그에다가 print하는 코드를 넣었다.

맨 마지막에 module_init과 module_exit함수로 init함수와 exit 함수를 지정해준다.

이 파일을 컴파일 하기 위하여 Makefile을 만든다.


(Makefile)


현재 로컬 머신의 버전과 일치하는 컴파일을 하기 위하여 -C 옵션을 주어 경로를 변경한 후 M 모듈은 현재 경로에다가 컴파일 하도록 한다.

make 를 입력하여 컴파일한다.


(컴파일 완료)


그러면 hello.ko 가 생기게 된다. 이 커널 오브젝트 파일은 모듈인데 이 모듈을 올려보도록 할 것이다.


(커널 모듈 올리기)


insmod 명령어로 커널모듈을 올릴 수 있다.

올라간 모듈은 lsmod를 통하여 확인 할 수 있다.


(올라간 모듈 확인)


올라간 모듈을 해제하는 법은 rmmod 이다.


(모듈 해제)


모듈을 해제하고 확인해보니 올라간 모듈 리스트에 없는 것을 확인 할 수 있다.

커널 로그를 확인해보겠다.

아까 우리가 init과 exit 에 printk를 넣었으니, 모듈이 올라갈 때 내려갈 때 로그가 찍혔을 것이다.


(로그 확인)


커널 로그의 위치는 /var/log/kern.log 이다.

이번에는 아까 만든 변수에 값을 넘겨주며 올려보겠다.


(인자 넘기기)


인자는 위와같이 넘길 수 있다.

이 올라간 모듈에 대한 정보는 어디있을까?
/sys/module에 들어가본다.

(모듈 확인)


올라간 모듈 hello에 들어가본다.


(모듈 정보)


모듈 정보들이 보인다.

여기서 우리는 parameters를 확인해 볼 것이다.



(변수 확인)


우리의 변수인 name이 보이고 안을 열어보니 Normaltic이라고 아까 우리가 입력한 값이 저장되어있는 것을 확인 할 수 있다.

이 값을 수정할 수 있을까 했더니


(파일 권한)


파일 권한은 읽기 권한으로만 되어있었다.

마지막으로 인자를 넘긴 모듈을 해제해보겠다.



(모듈 해제)


모듈 해제는 rmmod 로 같다.

그렇다면 kern.log는?


(커널 로그)


커널 로그에는 내가 넘겨준 인자와 함께 문구가 잘 찍힌 것을 확인 할 수 있었다.

LKM에 대해 간단히 이해를 해보았다.
RootKit을 만들고, 이해하는데 커널영역을 더 공부할 필요가 있을 것 같다.

참조
- http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/

'Hacking > System Hacking' 카테고리의 다른 글

(RootKit) Simple Rootkit  (0) 2018.04.12
malloc에서 사용하는 syscall  (0) 2018.02.21
System Hacking - jmp (반복문)  (0) 2017.04.19
System Hacking - jmp (분기문)  (0) 2017.04.18
System Hacking - 어셈블리어(사칙연산)  (0) 2017.04.14


이번 문제는 소스코드가 주어져서 쉽게 해결할 수 있었다.
소스코드가 없었다면, 이 취약점을 찾기 쉽지 않았을 것 같다.

소스코드의 select_menu() 함수가 문제였다.


(문제 코드)



(문제코드)


문제가 되는 함수의 부분이 위의 빨간 부분이다.
select_menu() 함수는 안에서 recursive하게 자기 자신을 호출한다.

화면에 다시 메뉴를 출력하게 할 때, while 문이나 다른 코드로 처리를 하는게 보통인데
참 특이하게 자기자신을 다시 호출하므로써 메뉴를 다시 출력하였다.

재귀적 코드는 깔끔해 보이는 장점이 있지만,
재귀가 많이 반복될 수록 스택 메모리를 많이 잡아먹는 현상이 일어난다.

그래도 함수 마지막에 호출하게하여 Tail Call(Tail Recursion)을 유도했지만,
실제로 gdb로 분석해본 결과 스택메모리를 낭비하며 동작하고 있었다.

그렇다면 스택을 쭉 낮출 수 있다는 것이다.
그리고 랜덤하게 주소를 받는데 그 주소가 스택 주소가 아니란 법도 없다.
더군다나, 여기서 만든 메모리할당 함수를 보면 실행권한까지 떡하니 줘버린다.
쉘코드를 써달라고 애원하는 듯 하다.

내가 exploit을 짜기 전 계획이다.
1. 스택영역의 주소를 얻는다.(계속 반복하여서)
2. 스택을 차곡차곡 쌓아서 주소를 팍 낮춘다. 우리가 구한 스택영역의 주소보다 낮게.
3. 그 다음 메모리할당을 한번 더 받아서 그 안에 쉘코드를 넣어둔다.(이 때 받은 쉘코드의 주소를 기억)
4. 아까 받은 스택영역의 주소에다가 쉘코드주소를 쫙 넣어둔다. 4096바이트만큼 쓸수 있으므로 1024개의 주소로 덮어쓸 수 있다.
5. exit 을 실행하면 스택이 recursive하게 리턴되면서 그 중간에 덮어쓰여진 쉘코드 주소로 뛰게 되고 쉘코드 실행!

exploit을 짜면서 바뀐 계획인데

2번의 과정은 없어도 되었다. 스택영역의 주소를 얻기위해 반복하는 과정에서 자연스레 엄청 낮아졌다.



(스택 영역 주소 구하기)


반복은 받은 주소가 현재 ebp 주소보다 커진 경우까지 반복한다.

그렇게 되면 ret,leave 하면서 쭉 내려갈 테니까!



(clear 함수)


반복하다보면 255개의 배열을 다 쓴다..
그런 경우 255개를 다시 다 지워주는 함수를 만들었다.
(계속 반복 할 수 있도록)

쉘코드 올리기


(쉘코드 올리기)


쉘코드를 올리는 부분이다.



(스택 영역 주소 덮기)


그 후 쉘코드 주소를 스택영역 주소에 덮어쓴다.

그리고 

exploit!



(스택 주소 받기)


엄청난 양을 받는다.

옆에 Stacking 옆의 값은 현재 ebp 값이다.


(clear 중)


clear 도 몇번 실행되었다.



(exploit)


끝내 만족하는 스택영역 주소를 받고
ret 주소를 덮어썼다.

여기서 5를 눌러 exit를 실행하면 최종적으로 shell을 획득할 수 있다.


(쉘 획득)


문제 풀이 끝!

'WarGame > 500 Project' 카테고리의 다른 글

(66/500) pwnable.kr - rsa_calculator  (1) 2018.03.10
(65/500) pwnable.kr - echo2  (0) 2018.03.08
(64/500) backdoor - Enter the matrix  (0) 2017.10.31
(63/500) pwnable.kr - syscall  (2) 2017.10.22
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01

+ Recent posts