zj3t

email: euntaejang@gmail.com

Latest Posts
밑에 사진처럼 0x16인줄 알고 버퍼오버플로우를 16진수 16를 10진수인 22로 바꾸고 sfp를

 제외포함하여 "\x90"*26을 줬는데 계속 실패했습니다. 

16진수가 아닌 10진수 16이였습니다.

저것만 아니면 1번문제와 동일합니다.





FTZ를 다풀고 LOB로 넘어왔습니다.
레벨 1이 해커스쿨 11번과 거의 유사했습니다.

FTZ와 다른점은 ssh가 아닌 telnet으로 접속해야한다는 것과 bash셸이 아닌 bash2로 문제를 풀어야한다는 점입니다. \xff를 bash에서는 \x00으로 인식한다고 합니다.

또한 gdb로 열었을 때 변수사이에 더미가 보이지 않았습니다.

쉬운 문제이므로 설명은 생략하고 과정만 올리겠습니다.




여기까지 입니다.

IMAGE_IMPORT_DESCRIPTOR 구조체의 위치(PE 바디에 위치)

>IMAGE_OPTIONAL_HEADER32.DataDirectory[1].Virtual Address =IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소


RVA가 7604 이다. 이는 .text섹션에 포함
RAW를 구하는 공식: RAW-Pointer to Raw Data = RVA - Virtual Address
text섹션 헤더의 Pointer to Raw Data=400
                     Virtual Address =1000

따라서.

RAW=7604-1000+400=6A04

어떻게 text 섹션에 포함된 줄 알았냐하면 text섹션을 봤을 떄 RVA가 1000이고 Virtual Size가 7748이라 7604가 이 안에 포함되기 때문입니다.


00007990: OriginalFirstThunk
FFFFFFFF:  TimeDateStamp
FFFFFFFF:  ForwarderChain
00007AAC: Name
000012C4: FirstThunk


-Name(임포트 함수가 소속된 라이브러리 파일의 이름 문자열 포인터)
RVA:7AAC RAW: ????

7AAC 또한 text섹션에 포함되어 있으므로 아까와 공식은 같다.

RAW=7AAC-1000+400=6EAC

따라서 RAW:6EAC이다.


comdlg.dll IID구조체의 첫번째 원소의 라이브러리 이름이다. 그 밑 두번째 원소의 라이브러리 이름도 이와 같은 방법으로 찾을 수 있다.

-OriginalFirstThunk(INT)

RVA:7990 RWA:6D90


저 빨간 박스가 INT이고, 각은 박스하나하나가 저번포스팅에서 언급한 IMAGE_IMPORT_BY_NAME구조체의 시작 주소입니다. 예를 들어 저기서 00007A7A를 보면 

RVA:7A7A RAW:6E7A


임포트하는 API의 이름을 볼 수있었습니다.

하나 더 보자면

RVA:7A5E RAW:6E5E

역시 임포트하는 API의 이름을 볼 수 있었습니다.

-FirstThunk

RVA:12C4 RAW:6C4


저 부분이 comdig32.dll 라이브러리에 해당하는 IAT 배열 영역이다. INT와 마찬가지로 구조체 배열로 되어있어 NULL로 끝난다.

지금 보이는 이 763249006, 763185CE 등은 의미 없는 값으로, 프로그램이 메모리에 로딩될 때 이 값은 정확한 주소 값(API의 주솟값 INT의 값)으로 대체됩니다.


ollydbg로 확인할 수 있다.


-출처: 리버싱핵심원리
















IMAGE_IMPORT_DESCRIPTOR=IMPORT Directory Table


typedef struct _IMAGE_IMPORT_DESCRIPTOR{
union{
DWORD characteristics;
DWORD OriginalFirstThunk; //INT address(RVA)
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
  DWORD Name;
DWORD FirstThunk; //IAT address (RVA)
}IMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME{
WORD Hint;
BYTE Name[1];
}IMAGE_IMPORT_BY_NAME;

-IAT에 값 입력 과정


1.IMAGE_IMPORT_DESCRIPTOR(IID)의 Name 멤버를 읽어서 라이브러리의 이름 문자열(ex:kernel32.dll)을 얻습니다.
2.해당 라이브러리를 로딩 ->LoadLibrary("kernel32.dll")
3.IMAGE_IMPORT_DESCRIPTOR의 OriginalFirstThunk 멤버를 읽어서 INT(IMPORT_NAME_TABLE)주소를 얻음
4.INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻음
5.IMAGE_IMPORT_BY_NAME의 Hint 또는 Name 항목을 이용하여 해당 함수의 시작 주소를 얻습니다.
   ->GetProcAddress("GetCurrentThreadld")
6.IDD의 FirstThunk 멤버를 읽어와 IAT 주소를 얻는다.
7.해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.
8.INT가 끝날 때까지(NULL을 만날 때까지) 위 4~7과정을 반복

-출처: 리버싱핵심원리(www.reversecore.com)
이번 포스팅은 저번에 말한 것 처럼 serial을 생성하는 알고리즘을 분석해보도록 하겠습니다.



저번에 프로그램실행화면 기억하시나요?? 


이런화면 이였습니다. 이 위의 두 개의 사진은 각각 About과 Check를 눌렀을 때 호출되는 함수의 부분입니다.

우리는 About함수는 필요없으니 Check함수만 알면됩니다.

제가 어떻게 Check함수를 찾았냐 하면 우리가 name에 대한 serial을 인증하려면 반드시 Check를 눌러야합니다. 따라서 저번 포스팅에 했던, serial을 찾으려했던 모든 부분이 다 Check를 누르면 호출되는 함수의 내부에 있는 조금한 함수입니다. 

이 Check함수를 찾은 목적은 알고리즘을 어디서 생성하는지 알지못하기 때문에 Check의 시작부분부터 차근차근 리버싱해보기 위함입니다.


이런 부분을 찾았습니다. 입력한 name, abcd는 각각 아스키코드로 61,62,63,64가 됩니다.

그리고 밑에 vbaFreeStr함수와 vbaVarAdd함수가 보입니다. FreeStr함수는 별게 없었습니다. 핵심은 VarAdd함수였습니다. 한번 실습해보시는 것도 좋을 것같습니다.


Add함수 내부로 들어가니 또 이런 Add함수가 보였습니다. F7로 다시 내부로 들어가 보도록 하겠습니다.



이 부분이 내부로 들어간 부분입니다. 주석만 보면 이해가 되실 거에요 값을 구하는데 쓸데

없는 주석도 몇가지 보이는데 대충 저 정도만 보시면 될것같습니다. 이미 abcd에 해당하는

 serial이 C5C6C7C8이라는 것을 저번 포스팅에서 구해놨고 이번에 a의 ASCII코드 값 즉 61

을 64를 더한 값이 C5고 b,c,d의 아스키 코드는 각각 a의 ASCII코드 보다 1,2,3씩 많은 값이

기 때문에 64와 더해서 serial을 생성한 다는 것을 알 수 있었습니다.








오랫만에 리버싱 포스팅을 하게되었습니다. 두달도 지난거 같아요 앞으로 리버싱위주로 많이 포스팅하겠습니다.

너무 오랫동안 안했어서 그른지 쉬운 crackme 문제도 삽질하면서 풀었습니다.  
이 예제는 리버싱 핵심원리에 있던 파일인데 이번엔 혼자서 분석해 보았습니다.



예전 codeengn에서 많이 보던 문제 유형입니다. 아마 예상으로는 name의 입력값에서 어떤 연산을 거친후 serial을 생성하는 거 같아요. 한번 보겠습니다.


문자열 검색으로 실패시 띄어지는 Messagebox에서 나오는 문자열을 검색했더니 저 TEST 명령에서 분기하는 것을 알 수가있었습니다. 저 TEST 명령 실행 후 ZF 값을 바꾸어 주면 성공 메시지로 분기하는 것을 보실 수 있을 것입니다. 

그렇다는건 TEST위의 함수 호출에 따라서 분기가 결정된다는 것입니다.
그래서 안으로 들어가서 탐색해 보았습니다.

cmp 함수가 보이네요 비쥬얼베이직에서 문자열을 서로 비교할 때 많이 쓰던 API함수 입니다.


더 들어가 보았습니다. 또 cmp함수가 보이네요. 들어가 보도록하겠습니다.

들어가서 F8로 조금 더 디버깅해보니 저 두개의 문자열이 나란히 뜨네요 아마 경험상 저게 serial일 것 같습니다. (123456789는 제가 입력한 serial입니다.)


성공!!!


다음 포스팅은 이 문제가 어떻게 serial을 생성하는지 serial을 생성하는 알고리즘을 분석해보도록 하겠습니다. 




오랫만의 포스팅입니다. 몇일동안 학교 시스템해킹 과제를 하느랴 FTZ를 못풀었었습니다. FTZ를 끝낸후 과제푼 것을 포스팅 해보겠습니다.



#include <stdio.h>

void printit() {
  printf("Hello there!\n");
}

main()
{ int crap;
  void (*call)()=printit;
  char buf[20];
  fgets(buf,48,stdin);
  setreuid(3098,3098);
  call();
}

이런 코드 입니다. 공격 방법은 간단합니다. 저 printit함수의 시작주소를 저장하는 call변수에 쉘코드가 입력된 환경변수의 주소를 써주면 공격이 성공하게 됩니다.









dummy 부분을 찾아내는 건 저번 포스팅 level16에서 찾은 방법과 동일합니다. 너무 쉽기 때문에 여기까지 포스팅 하겠습니다. 아마 지금까지 문제 푸신분들은 그림만 봐도 아실것입니다.


level16은 코드만 보면 조금 어렵게 느낄 수 있겠지만 레벨 15보다도 훨훨훨씬 쉬었습니다.

풀이 하겠씁니다~



#include <stdio.h>
 
void shell() {
  setreuid(3097,3097);
  system("/bin/sh");
}
 
void printit() {
  printf("Hello there!\n");
}
 
main()
{ int crap;
  void (*call)()=printit;
  char buf[20];
  fgets(buf,48,stdin);
  call();
}   

이런 코드입니다. 많이 긴가민가 하셨을 부분이 아마도 void (*call)()=printit; 부분이라고 생각 됩니다.

void (*call)()=printit; 부분은 C언어에서 함수 포인터라고 합니다.

함수포인터란 말 그대로 함수를 가리키는 포인터 입니다. 함수도 다른 변수들처럼 메모리에 저장이 되기 때문에 시작주소가 존재하고 그럼으로써 시작주소를 가리킬 수 있게 되는 것 입니다.

원형으로는

리턴타입 *변수명(인자)=함수의시작주소

그렇다면 저 코드를 보자면 | buf |dummy| call |dummy| crap | 과 같은 구조로 스택이 되어있습니다.
지금까지 문제를 푸신분들은 감이 오셨을 거에요 buf에서 입력을 받아 buf[20]을 채우고 dummy까지를 채운뒤 call변수의 값으로 shell()함수의 주소를 넣어주면 됩니다.

그럼 어떻게 shell함수의 시작주소를 알 수가 있을까요??

이렇게 gdb를 이용해서 함수의 시작주소를 찾을 수 있습니다. 

리눅스에서는 objdump라는 명령이 존재합니다. 이 명령으로 시작 주소를 찾으신 분들도 계셨습니다.


시작 주소는 모두 찾았습니다. 이제 해야할 건 공격코드를 만드는 것입니다. 
그러기 위해서는 dummy의 크기를 알아야하는데 노가다를 하기 싫어 약간의 실험(?)을 해보았습니다.


리버싱을 해서 그런지 어셈블리어에서 함수가 호출될 때 인자가 어떤 방식으로 들어가는지 를 알아서 대충 그려봤습니다.

즉 ebp-56이 변수 buf이고 printit()이 call변수에 저장되는 곳이 ebp-16이였습니다. 따라서 이 거리는 40이라고 알 수 있었습니다. 
햇갈리시는 분들은 위의 hint에 있는 attackme.c코드와 스택구조를 같이보시면 이해가 쉬우실 겁니다.



끝났습니다.

레벨 15가 저한테는 굉장히 어려워서 그런지 이 문제는 굉장히 쉬웠었습니다.