첫 번째 질문으로 카나리를 릭할 때 페이로드 보낸 다음 p.recvuntil(payload)
를 했었는데 두 번째 페이로드를 보낼 때는 위 과정을 거치지 않고 바로 read = u64(p.recvn(6) + b"\x00" * 2)
합니다. 두 번째 페이로드를 보낸 이후에 페이로드만큼 recvuntil()
함수로 출력(?)하고 데이터를 받아야 하는게 아닌가요??
두 번째 질문으로 공격 타겟이 되는 rop.c
소스에서는 입력하는 read()
함수가 총 두 곳인데 익스플로잇 코드에서는 두 번의 sendafter()
이후에 send()
를 한 번 더 입력합니다. 입력 횟수는 상관이 없는건가요??
전체 페이로드를 보지 못하여서 일단 제 예상만으로 답을 드리자면
첫 번째 질문은 이해하기 어렵습니다만, 첫번째 페이로드의 역할은 (첫번째 read 에 넣는 값은) 카나리 릭, 두번째 페이로드의 역할은 read 함수 재 실행 및 system 함수 호출을 위한 got overwrite, system 함수 인자 전달이 됩니다.
두번째 질문은 위에서도 설명하였지만, 두번째 read 이후 한번의 read가 더 필요하기에 rop으로 read 함수를 재 호출해야하며, 그렇기에 세번의 값 전달이 일어납니다.
첫번째 질문의 첫번째 질문부터 답변을 드리자면
문제 파일의 코드를 다시 보시면
// Leak canary
puts("[1] Leak Canary");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
와 같고,
read 함수에 canary leak을 위한 적절한 값을 삽입하고
printf 함수로 돌아오는 값의 형태를 보면
Buf: 문자열 + 입력한 payload + canary
가 될 것입니다.
이제 해당 문자열을 받아오는 페이로드를 보시면
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
cnry = u64(b"\x00"+p.recvn(7))
인데, 그 구조와 동일하게 받아오는 것을 보실 수 있습니다.
두번째, read의 주소를 받기 위한 주소의 경우도 마찬가지로 생각하시면 됩니다.
코드를 보시면
puts("[2] Input ROP payload");
printf("Buf: ");
read(0, buf, 0x100);
와 같고, read 함수 이후에 별다른 문자열은 출력되지 않지만, 페이로드에 의해 puts 함수를 실행시키고, 화면에 바로 주소를 뿌려주기에 해당 값을 받아오기만 하면 되기 때문입니다.
두번째 질문은 생각하신 것이 맞습니다.
rop이 가능한 케이스에서, 여러번 read 뿐만 아니라 지속적으로 다른 함수를 불러와 실행할 수 있습니다.
위에서 설명 드린 것과 같이, 문제에서는 값 전달이 총 3번 일어나야하는 상황인데, read 함수는 코드 내에 두번밖에 없으니 rop을 통해 한번 더 실행시켜주는 것입니다.