This part is for format string challenges.
realtime data monitor
Attachment: realtime-data-monitor
Description: When Xiao A was scanning a certain pharmaceutical factory, he found a large real-time database system. Xiao A realizes that the real-time database system collects and stores data of thousands of nodes related to industrial processes. As long as you log in, you can get valuable data. In the process of trying to log in to the real-time database system, Xiao A was unable to find a way to modify the login system key. Although she has now collected the value of the key that can log in to the system, she can only find other ways to log in.
Information
Check:
1 | $ file realtime-data-monitor |
Pseudocode:main
:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
locker
:
1 | int locker() |
imagemagic
:
1 | int __cdecl imagemagic(char *format) |
Analysis
A format string vulnerability in imagemagic()
has to be used to overwrite key
as 0x2223322
, and this happens to appear as the last example in Format String Vulnerability Intro (to reach Path 3
).
There I provided two payloads:
- the format built on my own – sort numbers from smallest to largest.
- use functions that other people provided – use integer overflow.
Clearly my own payload can solve this kind of challenge, here we have to write 0x02
, 0x22
, 0x33
and 0x22
to 0x0804A048
, 0x0804A049
, 0x0804A04a
and 0x0804A04b
, and the basic techniques I have already told before, however we have to get used to using corresponding module to be more effective (just want to slack off).
First we have to determine the parameter order of our input:
1 | $ ./realtime-data-monitor |
We can see our input aaaaaaaa
locates at the 12th parameter of format string (13th parameter of printf
function).
Then use payload to write exploit.
Exploit
It uses some codes/functions in the last example of Format String Vulnerability Intro.
1 | from pwn import * |
Using fmtstr_payload
of pwntools
has the same result.
1 | payload = fmtstr_payload(12,{key_addr:0x02223322}) |
Mary Morton
Attachment: mary_morton
Description: A very simple warm-up pwn.
Information
Check:
1 | $ file mary_morton |
Pseudocode:main
:
1 | void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) |
In the front of main
, sub_4009FF
is for setting alarm, sub_4009DA
is for printing context.
sub_4008EB
:
1 | unsigned __int64 sub_4008EB() |
sub_400960
:
1 | unsigned __int64 sub_400960() |
sub_4008DA
(not in the main logic):
1 | int sub_4008DA() |
Analysis
If you run this, you will see the interface and know what the vulnerabilities are:
1 | $ ./mary_morton |
In subfunction 1 (sub_400960
), read(0, &buf, 0x100uLL);
is the vulnerability, which reads too much and cause stack overflow. The tricky part is to overcome Canary
mechanism (aims to prevent stack overflow), and we have seen it in ADworld stack2 write-up.
But we still have chance, if we can remain the value of canary unchangable, program is also able to run normally.
In subfunction 2 (sub_400960
), printf(&buf, &buf);
is the format string vulnerability, we can use it to leak Canary
content, then rewrite it when performing stack overflow.
First of all, for format string vulnerability part, we have to indentify the order of Canary
(input aaaaaaaa,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p
):
1 | $ ./mary_morton |
From the output we can see our input (buf
) is the 6th parameter on the stack, while buf
is at the [rbp-90h]
and v2
(that is Canary
) is at the [rbp-8h]
, so it needs (0x90-0x8)/0x8 = 0x11 = 17
parameters more to get to v2
(Canary
). So Canary
is the 17 + 6
= 23rd parameter. Pyaload should be %23$p
.
Then we do stack overflow, see exploit for more detailed action.
Exploit
My former exploits always use recv()
to get output and do interaction. While using recvuntil()
to replace recv()
is a better way and will make exploit script common to both local and remote.
1 | from pwn import * |
greeting-150
Attachment: greeting-150
Description: None
It’s a format string vulnerability and is related to GOT
hijacking, and needs a little tip.
Information
Check:
1 | $ file greeting-150 |
Pseudocode:main
:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
getnline
:
1 | size_t __cdecl getnline(char *s, int n) |
Analysis
It has a classic format string vulnerability, but cannot be exploited by just writting some number to some place.
Since it has Canary
and enable the NX
, we have to use payload to get shell, then we think about hijacking GOT
item. Obviously we have to hijack some item as system_plt
, and the /bin/sh
still needs to input.
Only one time of input is not enough, we have to control the program flow for more input chances. But we cannot waste this one chance by just leaking ebp
or something, so we do not know base address of the stack, right? We cannot overwrite ret addr
as main
to drop into main
loop, but there is another choice: .fini_array
.
Before main
function, the codes in .init
and all func pointers in .init_arrary
will be called, and after main
the codes in .fini
and all func pointers in .fini_arrary
will be called. Overwriting 1st item in .fini_array
as start
will achieve the main
loop.
The whole process of a program (visit linuxProgramStartup for details):
So the first time we input, we hijack .fini
; when we back to main
we hijack strlen
in GOT
as system
, so that when we input /bin/sh
into this program, it happens to be the first parameter of strlen
(written as system
); then we get shell.
Sadly, after trying in this way I found that I can only go back to start
once. So we have to overwrite .fini_array
and strlen
in GOT
in one time.
Note: Using gdb
or gef
to debug it is fine, while using pwngdb
and peda
will meet an error:
1 | pwndbg> b main |
That is because the program forked a child process and it will mess up pwngdb
and peda
. The way to solve it:
1 | pwndbg> set follow-fork-mode parent |
Exploit
The limits for payload are:
- The length must be less than
64
- Can only return to
start
once (cant figure it out)
With the limit 1, our former gadgets like fmt_str
and fmtstr_payload
of pwntools
cannot meet the needs, so we have to write a more compact payload (with hn
to write every double bytes, instead of hnn
to write every byte).
1 | from pwn import * |