pushrbp brush in RTM
holyshield2025 문제를 풀면서, 얻은 지식을 정리하고자 한다.
일단, 기본적인 문제를 보자면
int __fastcall main(int argc, const char **argv, const char **envp)
{
vuln();
return 0;
}int vuln()
{
char buf[16]; // [rsp+0h] [rbp-10h] BYREF
read(0, buf, 0x19uLL);
return puts(buf);
}main → vuln 함수인데 vuln 함수에서 bof가 터지는 문제이다.
이런 형태에 문제면 rbp overwrite 한다음에 스택구조를 맟추는 문제인데 자세히 보면 main함수에 추가적인 명령어가 없어서 그런 접근을 아닌것 같았다.
0x19uLL 를 쓸수 있어서 RA를 1바이트 쓸 수 있다. 따라서, RTM으로 언제든지 main함수를 다시 실행시킬 수 있는데.. 일단 문제는 main함수를 다시 실행시켜도 정보를 leak할려면 vuln의 puts 함수로 가능한데 leak을 하더라도 프로그램이 종료가 되지 않게 할려면 결국 RA 1바이트를 덮어써야해서 그전이 내가 쓴 정보로 다 덮어져서 RA주소말고는 leak을 하기가 어렵다. 이때 필요한게 RTM poprbp brush 스킬이다.
그게 뭐냐면, RTM을 해서 main 함수의 에필로그 부분으로 가면 거의 필수적으로
.text:000000000000119A main proc near ; DATA XREF: _start+18↑o
.text:000000000000119A ; __unwind {
.text:000000000000119A endbr64
.text:000000000000119E push rbp다음과 같이 push rbp 가 존재하게 된다. 그런데 알겠지만, 이때 rbp 레지스터 값은 우리가 vuln에서 rbp 부분을 덮어쓰면서 우리가 control이 가능하게 된다.
따라서 저 부분으로 우리가 원하는 값을 스택에 집어넣을 수 있게 된다. 그러면 무엇이 가능하냐? 최종적으로는 원래는 main함수의 엔딩 프롤로그가 끝나면
.text:00000000000011A7 call vuln
.text:00000000000011AC mov eax, 0
.text:00000000000011B1 pop rbp
.text:00000000000011B2 retn프로그램이 종료가 되어서 우리가 계속 RTM으로 살리고 있었던 것인데,
poprbp로 스택을 만들어놓으면 저 main 함수의 엔딩 프롤로그가 실행이 되어서 다시 RTM을 할 수 있다. ( main 함수 RA 부분에 넣어놓으면 되니깐 ) 따라서 아까 문제였던 puts로 leak이 가능하게 된다.
이런식의 leak이 가능하고 지속적으로 main으로 돌아올 수 있다면? 이제는 사실상 끝이다. stack leak , libc leak을 할 수 있고 , 또한 이 기술로 ROP도 짤 수 있다.