3

I'm new to Software security and I'm studying it now at the university. I had some doubts about the Format String exploit, in particular how to count the length (in number of bytes) of a format string exploit.

Suppose that I have the following vulnerable code:

 int guess(char *user) {
     struct {
          int n;
          char usr[16];
          char buf[16];
      } s;

      snprintf (s.usr, 16, "%s", user);

      do {
          scanf ("%s", s.buf);
          if ( strncmp (s.buf, "DEBUG", 5) == 0) {
              scanf ("%d", &s.n);
              for ( int i = 0; i < s.n; i++) {
                  printf ("%x", s.buf[i]);
              }
          } else {
              if ( strncmp (s.buf, "pass", 4) == 0 && s.usr[0] == '_') {
                  return 1;
          } else {
              printf ("Sorry User: ");
              printf (s.usr); //#line 26 vulnerable line
              printf ("\nThe secret is wrong! \n");
              abort ();
          }
          }
      } while ( strncmp (s.buf, "DEBUG", 5) == 0);
  }

 int main(int argc, char** argv) {
      guess(argv[1]);
 }

And the code is compiled in an IA-32 architecture (32 bit) with cdecl calling convention and there's no attack mitigation implemented (no stack canary, no ALSR etc..., I'm in a complete vulnerable machine)

At line 26 there's a format string vulnerability since the placeholder is missing ( printf (s.usr); ).

I'd like to overwrite the EIP with the address of an environmental variable that contains my shellcode.

I'm supposing (this is a theoretical exercise, I'm aware that in practice there are many other implications) that the address of my environmental variable is 0x44674234, the address of the EIP is 0x42414515 and the displacement on the stack of my format string is 7.

So my format string exploit will be \x15\x45\x41\x42\x17\x45\x41\x42%16940c%7$hn%563c%8$hn, I'll place it into user and then it will be copied into s.usr and executed by printf (s.usr);

Now what I noticed is that only 15 characters are copied into s.usr from user.

Is my format string not exploitable? I counted 30 characters in my exploit, therefore the strcpy will copy only half of my exploit.

Is the number of characters I counted correct? How should I count them?

Mattia Surricchio
  • 1,362
  • 2
  • 21
  • 49
  • 1
    Is your question why `snprintf (s.usr, 16, ...)` only copies 16 bytes into `s.usr`? (It copies only 15, actually, since it saves space for a NUL.) – ikegami Jul 06 '20 at 19:03
  • 2
    I love everything about this post, your effort , the preciseness of your problem statement, etc. But putting the line numbers in makes it harder than it needs to be for someone to paste your code into their compiler and run it. You can mark `line 26 eg by marking it with a comment. +1 – ryyker Jul 06 '20 at 19:04
  • @ikegami No, my question was: 1) Am i still able to "perform" my format string exploit? Since only 15 character/bytes are copied inside s.usr. 2) Is my counting of the length of my format string exploit correct? Is it 30 bytes/characters long as i thought? – Mattia Surricchio Jul 06 '20 at 19:07
  • @ryyker Oh, i'm sorry, i'll edit the code! – Mattia Surricchio Jul 06 '20 at 19:07
  • 1
    Re "*my question was: 1) Am i still able to "perform" my format string exploit?*", Obviously no. If you need 30 bytes, and you have 15, it's not going to work. /// But if you're asking if it's possible to exploit it by some other means, then I don't know. Is 15 bytes long enough to jump to a location in the larger `user`? – ikegami Jul 06 '20 at 19:10
  • 2
    Re "*2) Is my counting of the length of my format string exploit correct?*", It is 30 chars/bytes long. You would need `snprintf (s.usr, 31, "%s", user);` to copy it all. – ikegami Jul 06 '20 at 19:13
  • @ikegami This is what i needed. You can answer if you want and i'll accept it – Mattia Surricchio Jul 06 '20 at 19:15
  • It's already game over at the line `scanf ("%s", s.buf);`: This writes attacker controlled characters without any checks into a stack based buffer, giving your attacker direct and unconditional control over the entire stack upwards from `s.buf`. The later `printf()` is totally irrelevant. If you want to write safe software, you must use input primitives that cannot overrun their buffers, or which allocate their buffer to fit. – cmaster - reinstate monica Jul 06 '20 at 19:57
  • @cmaster-reinstatemonica Yes i know that there is also a bufferoverflow vulnerability/exploit. My question was focused on the format string vulnerability part – Mattia Surricchio Jul 06 '20 at 19:59
  • That is to say, any `scanf("%s")` or `gets()` is an absolute no-no. Better get into the habbit of using `scanf("%ms")`, `getline()`, or `fgets()` instead. – cmaster - reinstate monica Jul 06 '20 at 20:02
  • @cmaster-reinstatemonica Sure! This is a vulnerable code on purpose! – Mattia Surricchio Jul 06 '20 at 20:04
  • If I were a thief and wanted to carry some money out of a bank, I wouldn't try to fool the AVM machine if the room with the money didn't have a lock. Computer security is like this: You need to get to the glaring mistakes first. The entire system is exactly as weak as the weakest part of it. And the `scanf()` is by far the weakest part of the program you gave. With such an asset presented on a silver tablet, an attacker won't even look as far as to find that `printf()` call. If I were an attacker, I would look for `gets()`, `scanf()` first. `printf()` formats come much later on the list. – cmaster - reinstate monica Jul 06 '20 at 20:39

1 Answers1

3

\x15\x45\x41\x42\x17\x45\x41\x42%16940c%7$hn%563c%8$hn does indeed refer to a sequence of 30 chars/bytes. snprintf (s.usr, 31, "%s", user); would therefore be needed to copy it.[1] The extra count is because snprintf reserves a space for a NUL.

Since you need s.usr to be the start of a sequence of 30 characters, and you can only place 15 of the necessary characters there, your exploit can't work as-is.

This doesn't mean that the bug can't be exploited. It may be possible to write a shorter exploit that jumps to the remaining exploit located somewhere else, e.g. in user.[2] But I don't have the necessary knowledge to asses the feasibility of this.


  1. Of course, you would also need a larger area in s.usr, at least under normal circumstances.
  2. user would contain <15-byte exploit><remainder of exploit>. The 15-byte exploit would jump to the remainder of the exploit.
ryyker
  • 22,849
  • 3
  • 43
  • 87
ikegami
  • 367,544
  • 15
  • 269
  • 518