이제부터 또 열심히! 

 

http://pwnable.xyz

 

pwnable.xyz

 

pwnable.xyz

에서 문제를 풀어보려고 합니다...

 

사실 며칠 전부터 풀어보려고 했는데... FTZ도 어렵고 그래서 ㅎㅎ 미루다가 FTZ level19 20이 너무 짜증나서 이거먼저 해보려고 합니다.

 

Welcome문제부터... 시작해보죠!

 


challenge 파일을 IDA에 넣고 디스어셈블 한 결과입니다.

IDA를 처음 써봐서 이런 방식의 표기는 너무 어색한데요...

 

알법한 부분만 좀 해석해보자면...

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  _QWORD *v3; // rbx
  __int64 v4; // rdx
  char *v5; // rbp
  __int64 v6; // rdx
  size_t v7; // rdx
  size_t size; // [rsp+0h] [rbp-28h]
  unsigned __int64 v10; // [rsp+8h] [rbp-20h]

  v10 = __readfsqword(0x28u);
  sub_B4E(a1, a2, a3);
  puts("Welcome."); // Welcome 출력
  v3 = malloc(0x40000uLL); // 0x40000만큼 동적 할당
  *v3 = 1LL; // 첫 바이트에 1을 입력
  _printf_chk(1LL, "Leak: %p\n", v3); // v3의 주소를 출력
  _printf_chk(1LL, "Length of your message: ", v4);
  size = 0LL; // size 변수를 0으로 설정
  _isoc99_scanf("%lu", &size); // size 변수의 값을 입력받음
  v5 = (char *)malloc(size); // size 만큼 동적 할당
  _printf_chk(1LL, "Enter your message: ", v6);
  read(0, v5, size); // size 만큼 v5에 입력받음
  v7 = size;
  v5[size - 1] = 0; // v5[size - 1]을 0으로 설정
  write(1, v5, v7);
  if ( !*v3 ) // v3이 0일 때 실행
    system("cat /flag");
  return 0LL;
}

이렇게 정리할 수 있을 것 같습니다.

 

그래도 모르겠으니 gdb를 통해 분석해봐야겠죠?

윽 symbol이 없다고 하네요...

 

이걸 어떻게 해결해야할 지 이리저리 찾아보다가!

 

1. 한번 r을 통해 실행해주고

2. b __libc_start_main을 통해 처음 시작하는 부분에 브레이크포인트를 잡아주고

3. 다시 r을 통해 실행해주면?

 

짜잔

이렇게 나오게 됩니다!

 

그리고 스샷의 맨 아래에 main=0x5555~~이렇게 있는 부분 있죠?

이곳이 바로 main 함수의 시작지점이라고 합니다.

 

그래서

x/70i 0x555555554920

이렇게 입력해주게 되면?

 

이런식으로 main 함수가 어떻게 구성되어있는지 확인할 수 있습니다.

 

그리고 일단 main 함수 첫부분에 브레이크 포인트를 잡아주도록 하죠.

 

이제 어떻게 풀어야할까요?

아까 위에 코드에서 

v5[size - 1] = 0;

이게 있었고,

if 문에서는 v3가 0이어야 하므로... 이걸 잘 이용하면 될 것 같습니다.

 

v5를 0으로 바꿔주는 부분에 브레이크를 걸어주고 실행해볼게요.

그게 이 부분이겠죠? if문과의 거리로 보나 생김새로 보나 저부분입니다.

 

브레이크를 걸어주고? 임의로 길이는 10 문자열은 AAAAAAAA를 넣어줬습니다.

 

rbp+rdx*1-0x1의 주소에 0x0을 입력하라.. 라는 의미인데요.

저 위치는 일단 배열의 마지막 원소를 나타냅니다.

rdx*1은 char 형 배열이기 때문에 1바이트 크기라서 그런 것 같네요.

우리의 목표는 저 배열의 마지막 원소가 v3이 되게하면 될 것 같습니다!

 

이제 각각 저 값이 무엇인지 알아봅시다!

우선 rbp는 레지스터에서 AAAAAAAA가 들어있네요. 입력한 문자열의 주소(v5 주소)가 들어가는 부분입니다.

rdx는 레지스터에서 0xa가 들어있으며, 이는 10을 의미하기에 문자열의 길이(size)가 들어가는 부분입니다.

 

따라서 (v5의 주소 + size * 1 - 0x1)가 v3이 되면 됩니다.

이걸 어떻게 하냐? 라고 생각이 드시는 분들도 있을겁니다.

 

v5의 주소도 모르는데 어떻게 잘 조절해서 저렇게 하지?

malloc의 문제점을 이용하면 되는데요. malloc은 동적 할당이지만 엄청난 크기(메모리 크기를 넘어가는 양)를 할당받게 된다면 할당에 실패하게 됩니다. 이 경우 동적 할당을 받는 변수의 첫 주소는 null(0)이 되게 되는데요. 바로 이 점을 이용하는겁니다.

 

(0 + size * 1 - 0x1 = v3의 주소) 처럼 될 수 있다는 것이죠!

 

 

이를 이용해서 문제를 해결하는 코드를 작성하게 되면?

이렇게 됩니다.

중간중간 print는 제가 사용한 문법이 맞는지 확인한 것이므로 무시하셔도 좋습니다.

 

이렇게 풀 수 있습니다!

끄읕

'Hacking-기초 > [PWN] Pwnable.xyz' 카테고리의 다른 글

[PWN] xor (50pts)  (0) 2020.08.01
[PWN] note (50pts)  (0) 2020.07.30
[PWN] misalignment (50pts)  (0) 2020.07.28
[PWN] add (50pts)  (0) 2020.07.28
[PWN] sub (50pts)  (0) 2020.07.28

이제 18번입니다!

이 문제도... 3개만 남았네여..

 

시작합시다!

 

코드는 이렇습니다.

 

이상한 함수가 많네요...

 

FD_ZERO: 두번째 인자의 모든 비트를 지웁니다.

FD_SET: 첫번째 인자에 해당하는 비트를 1로 합니다.

FD_CLR: 두번째 인자 중 첫번째 인자에 해당하는 비트를 0으로 합니다.

FD_ISSET: 두번째 인자 중 첫번째 인자에 해당하는 비트가 세트되어 있으면 양수값은 첫번째 인자를 리턴합니다.

출처

 

함수를 안보고 풀었지만, 정리할 때는 필요할 것 같아서 적어봤습니다.

 

 

코드를 보면 대충 read 함수에서 stdin으로 입력을 받고 입력받은 값에 따라서 switch문이 작동되는 것 같습니다.

 

변수 선언을 해 줄때 string check x count 순으로 진행됩니다.

그리고 만약 check가 0xdeadbeef라면 쉘을 실행하네요...

 

코드를 보면

switch 부분에서 case에 없으면, 그 값을 string[count]에 집어넣는다고 합니다. 이걸 이용하면 어떤 위치던지 데이터를 집어넣을 수 있을 것 같습니다.

 

우선 x가 우리가 입력받는 부분이고, string[count] = x 는 우리가 데이터를 마음데로 집어넣게 해주는 그런 부분입니다.

또한 shell을 실행할 수 있도록 해주는 if 문에는 check를 통해 검사를 하므로 string[count]를 통해 check를 바꿔야겠네요.

코드가 길어 gdb로 모두 하기엔 너무 많으므로 일단 여기까지만 보겠습니다. 대충 shell코드를 실행하기 위한 if문 부분까지인가 보네요.

 

변수 메모리는 모두 256바이트로 할당되어있습니다.

 

string[1]*100 + check[4] + x[4] + count[4] = 112이므로 나머지는 144바이트가 있는데요... 위치를 확인해봅시다.

 

check는 if문에서 사용하니까 그 부분을 보면...

이부분인거 같습니다. ebp-104부터 4바이트...

 

string[count]부분은 switch 내에 있으므로 그 부분을 보면...

대충 이부분인거같은데, cmp 개수를 통해서 switch를 구분해보면?

4번째 cmp부분일 테니까...

 

여기부터 어딘가까지인 것 같습니다...

 

x의 위치는 cmp를 통해서 ebp-252부터 4바이트 부분인 것을 알 수 있었네요.

 

아 참고로 je는 두 값이 같을 때 이동하는 것이고, jmp는 조건 없이 이동하는 것입니다.

이러면 저기부터 어딘가까지가 아니라 다시 찾아야겠네요...

찾아보니... 잘 이해는 못하겠지만 대충 저부분이 default 부분이고 while문의 끝이 되는 것 같습니다.

 

이부분을 보니 ebp-100의 주소를 eax에 넣고 eax를 ebp-252에 넣는다는 것 같습니다.

 

계속 찾아보죠

 

어셈블리에 익숙하지 않다보니까 자꾸 찾아보게 되는데

변수++ 변수-- 명령어가 어셈블리에서는 dec, inc로 구현되네요. 이를 이용하면 정말 빠르게 찾을 수 있을 것 같습니다.

 

우선 count++ 위에 string[count]가 존재하므로 count++를 찾아보도록 하죠!

 

 

대에충 이정도 부분인거 같습니다. count변수는 ebp-112부터 4바이트를 가지고 있다는 것도 알 수 있었네요.

ebp-100의 주소를 eax에 저장하는 것으로 보아하니... 저부분이 string 부분이 아닐까 하는 조심스러운 생각도 드네요.

 

일단 여기까지 알아낸 모든 변수의 위치를 적어봅시다... 기억이 안나요 ㅠㅜ

  • check:  ebp-104부터 4바이트
  • count: ebp-112부터 4바이트
  • x: ebp-252부터 4바이트

이렇게 3개를 알아냈습니다.

 

위의 사진을 천천이 알아보면...

 

다시

ebp-100의 주소를 eax에 입력

eax(ebp-100의 주소)를 ebp-252(x)에 4바이트 단위로 입력

ebp-112(count)의 값을 4바이트 단위로 edx에 입력

ebp-253의 값을 바이트 단위로 al에 입력

ebp-252(eax, ebp-100의 주소)의 값을 4바이트 단위로 ecx에 입력

al의 값을 바이트 단위로 edx+ecx(string의 해당 값의 주소)에 입력

 

이렇게 진행된 것 같습니다.

두번째 줄이 이해는 안가는데... 흐음...

 

암튼 ebp-100이 string[100]이랍니다.

이게 check랑 떨어져있는 거리가 4만큼이네요... check가 좀 더 앞에 있는거구요...

 

으아아아... 어셈블리 점점 헷갈리네요... 암튼 앞에 있는건 맞는거같으니까 저 차이만큼 \x08을 입력해주고 0xdeadbeef를 입력해주면 될 것 같습니다.

 

이렇게 풀기는 했습니다... 와 어셈블리 디게 헷갈리네요...

level 17입니다. 문제가 비슷비슷해서 할만하네요 ㅎㅅㅎ

 

호오... 이번에는 shell이 없습니다.

이제는 따로 넣어서 해줘야겠네요 ㅎㅅㅎ;;

구조는 gdb를 봐도 전 문제랑 거의 똑같으니까 넣지 않고 바로 풀이 넣겠습니다.

 

처음 시도입니다.

gdb로 \x90을 넣은 위치로 돌아가서 쉘 코드를 실행하려고 했지만... \x90의 개수가 부족해서... 실행이 안됩니다.

주소는 대충 잘 찾아서 넣었는데 말이죠...

역시 메모리 주소가 달라서 그런 것 같습니다...

아니면 뭐 여러가지 이유가 있겠죠.

 

두번째 시도는 11번 문제인가? 거기에서 시도했었던 환경변수에 등록하는 방식을 이용했습니다.

 

이렇게 c 파일을 만들어주고?

이렇게 했습니다.

 

환경변수에 등록하면서 알게된 것이 있는데.. 저는 Xshell을 사용하여 FTZ를 풀고 있어서 여러 터미널을 켜놓고 사용합니다. 근데 Xshell이라서 그런가 한쪽에서 입력한 환경변수가 다른쪽에서는 인식을 못하더라구요... 이거때문에 좀 해맨거 같습니다.

 

sh.c 파일로 he 파일을 만들었고, 이렇게 문제를 풀 수 있었습니다.

 

끄읕

 

 

level 16입니다.

이번 문제도 전 문제랑 비슷합니다.

 

이번에는 deadbeef말고 shell함수가 있네요.

 

빠르게 cp로 복사하고 gdb 해보겠습니다.

 

intel방식으로 바꾸고 main함수를 디스어셈블 한 결과입니다.

뭔가 짧죠?

 

shell도 디스어셈블 해보겠습니다.

 

이렇게 나옵니다.

이번 문제에서는 저기 shell+0 위치로 이동하면 됩니다.

주소가 나와있으니 전 문제랑 똑같게?

 

이렇게 풀 수 있습니다.

 

끄읕

level15입니다!

힌트를 보면?

 

이렇게 또 코드가 나옵니다!

level14랑 똑같은데 유일한 차이점이 check 변수가 포인터로 사용되고 있다는 점입니다.

 

여기서 할 수 있는 방법이 두가지가 생각나는데요...

1. 환경변수에 등록해서 deadbeef를 가져오는 방법

2. 코드 내에 deadbeef가 있으니까 그걸 찾는 방법이 있습니다.

 

환경변수는 귀찮으니까 코드 내에 있는걸 찾아서 해보겠습니다.

 

일단 복사해서 tmp에 넣은다음에 intel 방식으로 gdb에서 디스어셈블 해보았습니다.

보니까 cmp 부분(main+32)근처에 deadbeef가 있는것 같네요.

저기 찾았습니다. main+34에 있었군요.

이제 저 주소를 level14를 푸는 방식처럼 한 뒤 넣어주면 됩니다. deadbeef 대신해서요.

나머지는 다 같으니까 빠르게 끝내겠습니다.

 

끄읕!

 

+ Recent posts