Reading Memory State.
above code exploits printf(...) instruction's behavior.
to instruction / function call 'printf' there's passed data coming from user, who can pass anything there in this case, including formatting string.
%x in this case means reading 4 of 8-bit bytes, a '32-bit long word', with bit-string data being interpreted as a 'hexadecimal number'.
since we did pass only formatting string, without providing any data as is 'proper', without putting any data on stack, other of our data is examined. program examines stack, including the 'ABCD' part - written in a reverse hexadecimal notation it's read as: '44434241'.
the trick is knowing how many of '-%x' to put, how to reach to proper memory address.
Locating memory address of a variable.
if we can edit code, we can also see memory address of any variable, with a little of experiments.
(later we'll use a debugger to modify code as it's run).
above code 'looks up' a variable & it's value under a known memory address.
second image uses a program from first image to do a lookup & uses different notation to do 'the same' thing.
there's noticeable change of different memory address of a variable in a program being run - probably new process was assigned different memory space.
in this Hacking School's Operating System (SSO 2.0) this doesn't happen too often, in other Operating Systems it might.
Writing to a Memory.
above code writes to memory.
a value of '5' was written, according to formatting string's '%n' parameter specification: four bytes and a minus '-' character, totally a five of 'byte-lenght characters'.
how to write a larger number ?
depending on how large that number is, a method can be chosen - either simple to understand, remember & use ... or complex, but handling larger of numbers better.
writing a '1000' in an 'i' variable.
'1000' in a decimal notation equals '0x3e8' in a hexadecimal notation.
the above method worked for writing a '1000' number quite well, but perhaps there are various problems associated with writing larger numbers using this method.
1000 bytes were composed of 4 of '32-bit-byte-length' address numbers, 2 of '-' characters & 994 of '32-bit-byte-length' %x signs. each of '%x' signs prints an 'empty character' on screen.
in x86 architecture with 32-bit Linux Operating System, a character variable as minus '-' or 'empty character' for example, has size of 32-bits, a single 32-bit length byte.
%n ($n in this case as well) writes to memory amount of written characters within this printf's instruction call, see a proper 'man page' for details. command to do so is: 'man printf 3' on a Linux shell.
what about the 'better' method ?
for example: let's consider a '0x88664422' = '2288403490' number.
let's look at following images ...
writing first four bytes.
it's 'reverse hexadecimal' notation where least important numbers come first.
first number is 34 - 16 (number of bytes written already, memory addresses) = 18.
we put 18 then before %x%9$n
filling-in with rest of a number.
instead of producing very many characters to be used with %n, we've decided to split an 16-byte-long-number into 4 parts, then fill each part separately.
let's consider another number: '0x22446688' = '574908040'.
writing '0x22446688' into 'i' variable.
let's notice that putting '1' before a '2-digit hexadecimal number' is "shifting 'it' by 16*16 = 256 places" in memory.
for example: '0x166' is '256-places shifted 0x66'.
i've also checked this method with another number: '0x33113399', still works fine.
Attack on a 'protected application'.
let's consider following program, as on image:
user 'protected' application with login & password, but made error of using 'printf' function in an insecure way.
we've compiled this 'a program' with -ggdb compilation parameter, to enable debugging.
let's check how many 'places on stack' we have to 'get through' to reach a password ...
13 of '%x' signs are needed to get to a password.
... 13th place on main function's stack is 'the password' as is entered by user running this program.
now let's use debugger to locate 'ok' variable's memory address.
we've compiled program with -ggdb parameter, this simplifies that task a bit, otherwise it'd be a little harder.
let's run a debugger with a command: 'gdb fm5'.
debug & attack by overwriting 'ok' variable's state.
after few experiments with format string attack in & around an address of 'ok' variable we've did overwrite of 'ok' variable with a value != 0, a 'truth' value in C.
program was hacked, we've got into protected area.
we've noticed that not always address given by a debugger is accurate, it seems it's two different processes anyway - with different memory addressing.
Attack using a 'shellcode injection'.
let's look at following image, it's mostly debugger use to change program's behavior.
debugger used to change program's behavior.
we've changed a value of EIP (instruction pointer) register as it was put on stack & displayed with 'info frame' debugger command.
this enabled us to change return address from main function to an address pointed by a 'shellcode' variable, with another debugger command, as on image.
the 'shellcode' program was ran & it caused hacked code to run a command shell, instead of just ending the application's run 'as it should'.
(to complete this article, have to learn something from book  ... chapters 6, 7 & 8 are neccessary to understand chapters 9 & 10. will do this now & share progress in a separate post).
post under construction, more to be written & elaborated later, including 'remote attack').
see also if You wish or need ... : About 'format string' attacks, 'Buffer Overflow' Hacking Attack, 'Heap Overflow' Hacking Attack.
(still having mental blocks preventing me from learning that way of hacking).