후우우... 정말 어렵네요...ㅠㅜ

 

www.pwnable.xyz  

입니다.

 

xor 문제고요...

 

문제 풀이 시작합니다!

 

어김없이 IDA 분석으로 시작합니다.

변수가 여러개 선언되네요.

 

그리고 계속 반복하는데?

v6 = 0으로 설정해주고

v4 v5 v6의 값을 받아오며, 이 인자의 개수는 v3에 저장됩니다.

scanf의 반환값이 입력받은 변수의 개수이기 때문이죠.

 

그리고 if부분에서 v4 v5 v6은 모두 비어있거나 0이면 안되고, v6은 9보다 작거나 같아야하며, v3은 3이어야합니다.

만약 그렇지 않다면 break를 통해 프로그램이 끝나게 되네요.

 

무사히 계속 진행한다면 v5와 v4를 ^비트연산(XOR 연산)을 진행하여 result[v6]에 입력합니다.

그리고 이 값을 출력하기까지 진행되네요.

 

여기서 볼 수 있는 점은 v6이 음수가 될 수 있으며, 이 값으로 데이터를 넘나들 수 있다는 것이 되겠네요... 권한의 문제는 고려하지 않고서요.

 

앞에서 풀었던 add문제나 misalignment문제와 살짝 비슷한거같기도 합니다.

 

다음은 gdb로 확인해봅시다.

 

문제를 좀 대충 풀었던거같아서... 이것부터 이제 다시 확인하고 지나가겠습니다.

 

CANARY

-랜덤 값인 canary를 ebp와 지역변수 사이에 위치시킵니다.

-함수가 시작될 때 이 값을 저장하고, 끝나기 전에 변조여부를 확인합니다.

-이 보호기법이 활성화되어있다면 스택 영역을 건들이기 어렵겠네요...

 

FORTIFY

-일반적인 메모리 버퍼에서 발생하는 BOF를 잡기 위해 존재합니다.

-입력된 데이터의 크기를 확인하여 잡아내는 것 같습니다.

-이 보호기법이 활성화되어있다면 BOF를 일으키기 어렵겠네요.

 

NX

-Non Excutable의 약자로 실행시키지 않는다는 의미입니다.

-BOF에 이용되는 메모리 공간에 있는 코드를 실행시키지 않는 보호기법입니다.

-이 보호기법이 활성화되어있다면 메모리공간에서 쉘 코드를 사용할 수 없습니다.

 

PIE

-위치 독립 코드로 이루어진 실행파일이라는 뜻입니다.

-바이너리의 주소를 랜덤화하여 바이너리의 특정 주소를 수정하는 것과 같은 공격을 방어합니다.

-실행시킬 때마다 주소가 바뀌게 됩니다.

 

RELRO

-Relocation Read-Only의 약자입니다.

-크게 NO RELRO, PARTIAL RELRO, FULL RELRO로 구분됩니다.

-NO RELRO는 ELF 기본 헤터, 코드영역을 제외한 거의 모든 영역에 RW 권한을 주게 됩니다. 

-대부분의 공간에서 데이터를 바꿀 수 있습니다.

-Partial RELRO는 NO RELRO에서 _Dynamic 섹션에 쓰기 권한을 없앤 것입니다.

-FULL RELRO는 bss 영역을 제외한 모든 영역에 write 권한이 없어집니다.

 

이렇게 정리할 수 있으며, 이와 동일한 내용을 Pwnable Summary에도 올리겠습니다.

 

NX PIE RELRO 이 세가지의 보호기법이 사용되고 있습니다.

 

vmmap을 통해 확인해봅시다!

실행시키기 전 모습입니다.

저기 모든 부분에 read 권하는 기본적으로 존재하며,

0x7c0~0xb9d에는 실행권한,  0x00201d90~0x00202250부분에는 쓰기 권한이 또 존재합니다.

실행을 하니까 코드 영역에 rwx 모든 권한이 존재합니다. 이 부분을 이용하면 될 것 같습니다.

 

 

 

디스어셈블 한 결과입니다.

주소를 비교해본 결과 vmmap에서 rwx를 모두 가지고 있는 부분에 속해있다는 것을 알 수 있습니다.

 

 

우리의 목적은

1. result[v6]에서 v6의 값을 음수로 잘 조절해 코드 영역까지 이동한다.

2. 이동한 코드영역은 exit가 사용되는 main+148 부분이다.

3. 그 부분에서 원래 실행되려던 부분을 대체하고 win 함수의 주소를 넣는다.

프로그램을 끝내면 cat flag가 실행될 것이다.

 

입니다.

 

그러기 위해서는 result에서 call까지 얼마나 떨어져있는지 계산해야 합니다.

 

 

result는 0x202200에 위치해있습니다.

 

call exit는 0xAC8에 위치해있습니다.

 

이를 통해서 v6를 계산하면.

QWORD, 즉 8바이트가 하나의 변수를 이루고 있기 때문에

result + v6*8 = exit

이어야합니다.

따라서 이를 잘 넣어서 v6를 계산하게 되면?

 

v6 = -262887이 됩니다.

 

곧바로 사용하기 전에...

이곳을 보면, 각각 입력받은 값을 rax rcx rdx에 저장하고 rcx(v4) rdx(v5)를 XOR 계산한 뒤 rcx에 저장하게 됩니다.

rdx는 저장할 데이터의 위치를 계산하기 위해 rax(v6)에 8바이트를 곱한 값을 저장하고

rax에는 rip(현재 가르키고 있는 주소)+0x201715를 계산한 값을 저장합니다.

그런 뒤 rdx + rax인 위치에 xor 계산한 값을 저장하게 됩니다. 

 

이제 한번 값을 입력해보고 실행시켜보겠습니다.

 

 

이렇게 입력했으니 xor된 값은 1이 될 것입니다.

 

우선 위치가 제대로 되었는지 확인하기 위해 

데이터를 입력하는 부분(result[v6] = XOR 결과)에서 rdx+rax를 찾은 결과

정확하게 main+148 부분인 것을 알 수 있습니다.

또 입력받는 rcx도 찾은 결과

0x1이 나오는 것을 알 수 있습니다.

 

아직까진 정말 정확하게 진행되고 있습니다!

 

이제 저 main+148부분에 원하는 값을 입력해야 하는데요.

 

위에 사진을 보면 0x458b48fffffd63e8이라고 값이 적혀있습니다.

여기서 call exit는 총 5바이트로 0xfffffd63e8입니다.

e8은 call의 opcode를 의미한다고 합니다. 이는 상대적인 경로일 때 사용되며, 절대경로일때에는 ff를 사용한다네요.

상대적인 경로는 RIP-relative Addressing 방식으로 부여된 경로를 사용합니다.

RIP-relative Addressing이란 RIP에 저장되어있는 레지스터 값과 32비트의 operand를 통해서 64비트의 주소 공간을 나타내는 방식입니다.

operand란 opcode와 함께 사용되는 주소로 위의 주소에서 0xfffffd63이 바로 operand입니다.

 

이를 통해 주소를 나타내는 방식은 rip+operand를 하여 이 결과가 실행하려는 함수의 주소가 되면 됩니다.

 

따라서 우선 rip를 구해보면 operand는 0xfffffd63이고, exit 함수의 주소는 0x830입니다.

따라서 rip는 0x830-0xfffffd63으로

0xacd의 값이 rip가 됩니다.

따라서 win 함수로 이동하기 위한 operand를 구해보면,

win 함수의 주소는 0xa21이기 때문에, win - rip를 하면

 

이렇게 0xffffff54라는 결과값이 나오게 됩니다.

 

따라서 우리가 입력해야 할 값은, 

0x458b48fffffd63e8 에서 operand 값만 바꾼 0x458b48ffffff54e8이 됩니다.

 

이제 우리는 이 값을 xor 계산을 하여 넣어야합니다.

 

첫 번째 인자는 1을 넣는게 제일 편하고

두 번째 인자는 입력해야할 값을 첫 번째 인자와 xor 한 값을 넣어야합니다.

세 번째 인자는 위치를 나타내는 값인 -262887을 넣어주면 됩니다.

 

첫 번째 인자와 두 번째 인자는 서로 위치가 바뀌어도 되며,

첫 번째 인자와 xor 한 값을 두 번째 인자에 넣어줘야 하는 이유는

a xor b = c라고 할 때

a xor c = b, c xor b = a의 결과가 나오기 때문입니다.

 

이제 최종적으로 입력해봅시다.

 

이건 굳이 payload를 만들어서 실행할 필요가 없을 것 같습니다.

 

그냥 바로 입력해버리면

첫 번째 인자를 1

두 번째 인자를 0x458B48FFFFFF54E9(첫번째 인자와 xor함)를 정수로 바꾼 값인 5011179274728592617

세 번째 인자를 -262887

이렇게

1 5011179274728592617 -262887

를 입력하면 됩니다.

 

실행해본 결과 성공했다는 것을 알 수 있습니다.

처음에 값을 집어넣어 call exit의 주소를 바꿔주고,

두번째에는 0 0 0을 넣어 원래 call exit를 실행하게 해주면 되는 것이죠.

 

nc로 실제로 해보겠습니다.

끝났습니다!

 

허우... 정말 어렵네요... 이해하면서 푸는게.......

'Hacking-기초 > [PWN] Pwnable.xyz' 카테고리의 다른 글

[PWN] GrownUp (50pts)  (0) 2020.08.07
[PWN] note (50pts)  (0) 2020.07.30
[PWN] misalignment (50pts)  (0) 2020.07.28
[PWN] add (50pts)  (0) 2020.07.28
[PWN] sub (50pts)  (0) 2020.07.28

+ Recent posts