xor Write-Up
pwnable.xyz의 xor 문제를 풀어봤다.
배점이 50점이라 난이도가 높지는 않고, 단순 Overflow Exploit이 아니라서 괜찮은 문제같다.
Binary
먼저 64Bit 바이너리이고, Shared Object로 PIE가 걸려있음을 알 수있다.
보호기법은 Stack Canary를 제외하고 모두 적용되어 있다.
Full RELRO이기 때문에 GOT Overwrite가 불가능하고, NX Bit가 적용되어 있기 때문에 실행권한이 없는 상태이다.
그리고 win( )로 Flag를 출력해주는 Oneshot함수가 존재한다.
또한, 바이너리 코드영역에 rwx권한이 부여되어 있다.
바이너리 특이사항은 이정도로 잡으면 될 것 같고, Exploit Point는 win( )와 코드영역 rwx권한으로 잡으면 된다.
Solve
main( )를 보면 %ld로 부호있는 10진수를 3개 입력받고, 그 중 v6는 Index를 참조하는데 사용된다.
v4와 v5는 서로 xor한 값을 result[v6]에 저장하는데, 입력값 / 저장주소 모두 사용자 입력 값으로 정해지기 때문에
Arbitrary Write가 가능하다는 점을 캐치해주면 된다. 정확한 분석은 Assembly를 보면서 진행해보자
마지막 라인 'mov [rdx+rax], rcx'가 Arbitrary Write 부분이다.
해당 구문은 풀어서 써보면, 'v6*8 + &result = v5 ^ v4'으로 원하는 주소에 원하는 값을 쓸 수 있게 된다.
아까 위에서 코드영역에 쓰기권한이 있는걸 확인 했으니, 적절한 위치에 win( )를 호출하는 쉘코드를 넣어주면 된다.
바이너리 내부에 while문이 있기 때문에, 모든 구문 실행을 완료하면 다시 특정 주소로 되돌아간다.
따라서, 0x555555554a5c 이후 주소에 쉘코드를 넣어주면 되는데, 나는 0x555555554a70에 쉘코드를 저장해줬다.
먼저 주소 계산을 통해서 정확한 offset을 구해준다.
result 주소(0x555555756200) - 쉘코드 저장주소(0x555555554a70) = 0x201790 이라는 결과가 나오고 중간에
v6*8 연산이 있기 때문에, 결과값을 8로 나눠줘야 한다.
따라서, v6에 -262898을 입력해주면 'v6*8 + &result'가 0x555555554a70을 참조하게 될 것이다.
주소 계산은 정확히 해줬으니 이제 쉘코드만 저장해주면 된다.
쉘코드는 'call win( )[e9 ac ff ff ff 90 90]'으로 win( )를 호출하는 코드를 사용하였다.
v4와 v5가 서로 xor연산을 하기 때문에 해당 조건을 고려해서 값을 입력해줘야 한다.
v5값을 0으로 주면 v4값에 영향이 없겠지만, 위 if문에서 예외처리를 하고 있기 때문에 0이 아닌값을 넣어줘야 한다.
쉘코드를 10진수로 바꾸면 40691825832340713이고, 40691825832340712를 1과 xor 해주면 정확한 쉘코드를 입력할 수 있다.
쉘코드가 정확히 입력되어 0x555555554a70주소가 call win( )으로 바뀌었다.
문제 서버에 접속해서 '1 40691825832340712 -262898'을 입력해주면 win( )가 실행되어 Flag를 얻을 수 있다.