tag:blogger.com,1999:blog-67611416722126937332024-03-05T03:35:40.699-08:00Software Development, Etc.Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-6761141672212693733.post-27895824012080783782023-08-16T21:24:00.001-07:002023-08-16T21:26:15.029-07:00DEF CON 31 - Payments Village - Card Hacking Challenge Write-up<p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgd0ieAJwgSBa6Geq_Dac-48f7pnxLOx_sMS2d4oYJaEICer8OqWgqu02EIzfPOhw3U_5IvQlRn9ko9Pbls13btPOe_gmpB1mHybYjd1VAHG-Ct-RAkNd_q6Fwl4ougNkduL-j08xyq9_5yLnQLvEo3I9Q3jMwjjbts8wervjZAdhxsrDt0lGkWU-hWlkFh" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="3024" data-original-width="4032" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEgd0ieAJwgSBa6Geq_Dac-48f7pnxLOx_sMS2d4oYJaEICer8OqWgqu02EIzfPOhw3U_5IvQlRn9ko9Pbls13btPOe_gmpB1mHybYjd1VAHG-Ct-RAkNd_q6Fwl4ougNkduL-j08xyq9_5yLnQLvEo3I9Q3jMwjjbts8wervjZAdhxsrDt0lGkWU-hWlkFh" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Payment Village Issued Credit Card</td></tr></tbody></table>At DEF CON 31 this year the Payments Village put on a <a href="https://www.paymentvillage.org/card-hacking-challenge" target="_blank">Card Hacking Challenge</a>. You were provided with a working credit card, a point-of-sale terminal (physical, or as an Android APK), and the village was running a "bank" server at www.paymentvillageprocessing.com.<p></p><p></p><p>There were 3 main goals:</p><p></p><ol style="text-align: left;"><li>Steal $100,000 from the card</li><li>Commit other fraudulent operations</li><li>Steal fifteen cards from the SoftPOS</li></ol><p></p><h2 style="text-align: left;">Setup</h2><p>With data flowing through 3 hops in this challenge, I had my choice of intercepting NFC traffic between the card and the POS, modifying the APK itself, intercepting traffic between the POS and the server, and/or directly attacking the server.</p><p></p><p>I chose to intercept (and modify) HTTP traffic between the POS client and bank server. First I setup the provided <i>SoftPOS.apk</i> on an Android phone (side loaded via <span style="font-family: courier;">adb</span>). Then I setup the interception proxy using Burp Suite. </p><p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgg3zF14SoJJSPi8QK2xu_68sQ8FCmtwkNHgxdxHj4TLGAsZ1X7jSDvcFgKhWEs-FZnA0lDptrobpgd1l-xySK5DAwdoDh2N7bVgSytFAV9IDxB4nDwi-AImCoLV21tfCF_lE_xk6FSrv7X1dmhsk6Sn0zo8miiWD258pCy_KvinRah8tiXiY6Y7w4t1uWl" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="1920" data-original-width="1080" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEgg3zF14SoJJSPi8QK2xu_68sQ8FCmtwkNHgxdxHj4TLGAsZ1X7jSDvcFgKhWEs-FZnA0lDptrobpgd1l-xySK5DAwdoDh2N7bVgSytFAV9IDxB4nDwi-AImCoLV21tfCF_lE_xk6FSrv7X1dmhsk6Sn0zo8miiWD258pCy_KvinRah8tiXiY6Y7w4t1uWl" width="135" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">SoftPOS main screen</td></tr></tbody></table>Step-by-step:<p></p><p></p><ol style="text-align: left;"><li>Phone 1: Setup WiFi tether to mobile network so all devices can talk to each other</li><li>Laptop: Connect to WiFi tether on Phone 1</li><li>Laptop: Listen with Burp Suite proxy on WiFi IP</li><li>Laptop: Open up firewall to allow incoming connections</li><li>Phone 2: Connect to WiFi tether</li><li>Phone 2: Configure proxy on that WiFi connection</li><li>Phone 2: Restart SoftPOS to pickup new proxy settings</li><li>Phone 2: Run a payment through SoftPOS and it shows up in Burp Suite on laptop</li></ol><h2 style="text-align: left;">Capture HTTP Traffic</h2><p></p><div>With the HTTP proxy in place we can view what successful and unsuccessful payment attempts look like. A successful payment sends multiple fields as HTTP query args in a GET request.</div><div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEglNviN2ltjJfvqGgooil2r0mAB3yi12nqqP6D1g8Q-FBEuCRnmaGrcY9VqFfWnpORNrsHBGK5hsxT7diOV6sfRDWXMIu-04FNtTQ4UZS7Nu3n1ofVLRTEDk0IyqN4WM-qZY_Ug1xdBgVZ4mAAg6Vo9PntrgUgcPJJX-Dkv1dRgRYIJV_DUIwwyDQ4gDPPi" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="139" data-original-width="584" height="152" src="https://blogger.googleusercontent.com/img/a/AVvXsEglNviN2ltjJfvqGgooil2r0mAB3yi12nqqP6D1g8Q-FBEuCRnmaGrcY9VqFfWnpORNrsHBGK5hsxT7diOV6sfRDWXMIu-04FNtTQ4UZS7Nu3n1ofVLRTEDk0IyqN4WM-qZY_Ug1xdBgVZ4mAAg6Vo9PntrgUgcPJJX-Dkv1dRgRYIJV_DUIwwyDQ4gDPPi=w640-h152" width="640" /></a></div><br /></div></div><div><div>These <a href="https://emvlab.org/emvtags/" target="_blank">query parameters</a> break down to:</div><div><br /></div><div><span style="font-family: courier;">amount</span></div><div><span style="font-family: courier;">trans_type</span></div><div><span style="font-family: courier;">9f15 (Merchant Category Code)</span></div><div><span style="font-family: courier;">9f34 (Cardholder Verification Method Results - CVM)</span></div><div><span style="font-family: courier;">9f36 (Application Transaction Counter)</span></div><div><span style="font-family: courier;">9f26 (Transaction Cryptogram)</span></div><div><span style="font-family: courier;">82 (Application Interchange Profile)</span></div><div><span style="font-family: courier;">5a (Application Primary Account Number - PAN)</span></div><div><span style="font-family: courier;">57 (Track2 Equivalent)</span></div><div><span style="font-family: courier;">9f37 (Unpredictable Number)</span></div><div><span style="font-family: courier;">5f2a (Transaction Currency)</span></div><div><span style="font-family: courier;">9a (Date)</span></div><div><span style="font-family: courier;">9f02 (EMV Amount Field)</span></div><div><span style="font-family: courier;">type</span></div><div><br /></div><div><b>Note</b>: There are 2 different "type" fields and 2 different "amount" fields, which will be important later.</div><div><br /></div><div>Here's a successful response:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg5wnnNASAICDQQS9Rff25J_jyEbRPZTwiZakbji29uXsHURHb81OQgKBRDngk8t_6iLNLzCjpPaTmyAo3_7yJCgBgzvSlvboNhaPVu0GT8lwxYb2L7K0sv3MAFH8ri1lmETx8iK3JARTDU94JzwM6r8jmIoCIHQL87Tw-LjNhkEBhWO2sdvZuooxCBo2LG" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="259" data-original-width="535" height="310" src="https://blogger.googleusercontent.com/img/a/AVvXsEg5wnnNASAICDQQS9Rff25J_jyEbRPZTwiZakbji29uXsHURHb81OQgKBRDngk8t_6iLNLzCjpPaTmyAo3_7yJCgBgzvSlvboNhaPVu0GT8lwxYb2L7K0sv3MAFH8ri1lmETx8iK3JARTDU94JzwM6r8jmIoCIHQL87Tw-LjNhkEBhWO2sdvZuooxCBo2LG=w640-h310" width="640" /></a></div><br /></div></div><div><span style="background-color: #eeeeee;">đź’ˇ </span><span style="background-color: #eeeeee;">Thank you Payment Village organizers for keeping the traffic over HTTP so that I could learn about the EMV protocol and not spend half a day debugging TLS MitM issues!</span></div><div><span style="background-color: #eeeeee;"><br /></span></div><h2 style="text-align: left;">Challenge 1 - Steal $100,000 from Card</h2><div><div>Let's get hacking. We need to spend $100,000 so let's run a single transaction for that amount with the card on the POS.</div><div><br /></div><div><b><span style="color: red;">Error</span></b>: The POS app won't let us charge more than $10 at a time.</div><div><br /></div><div>Ok, let's intercept the HTTP traffic of a successful $10 payment, then replay the payment packet to the server changing the amount from $10 to $100,000.</div><div><br /></div><div><b><span style="color: red;">Error</span></b>: The server won't let us replay packets.</div><div><br /></div><div>Each request has an incrementing counter in field <span style="font-family: courier;">9f36</span>, so the server knows if a request is replayed. Let's increment that ourselves and replay it.</div><div><br /></div><div><div><b><span style="color: red;">Error</span></b>: Auth failure. We've changed a field that is signed by the cryptogram. i.e. the server can detect that the packet was tampered with.</div><div><br /></div><div>We can <a href="https://emvlab.org/cryptogram/" target="_blank">recompute the cryptogram</a> if we can extract some key material from the CHIP on the card. But we have a shortcut! The bank server, when receiving a bad cryptogram erroneously returns the expected cryptogram in the <b>AC</b> field of the error message.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiBiQmBo3hQ0w65zOHVjZnydjvlR8aAgzazFVhSeKjhFcF6BifvbukDEczjLT-ayCdP9ps9DnTlhK-gbHkLVLvV7f-0RPDImSViSQdr2t7PUUdU7iHH8DOBUKWDu75hn6mDNv3HfH0Wo8WabogfXGFwq-RA49PN22bDPOD7xWUim2vwecV0K9cU1_CL2OFI" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="73" data-original-width="369" height="79" src="https://blogger.googleusercontent.com/img/a/AVvXsEiBiQmBo3hQ0w65zOHVjZnydjvlR8aAgzazFVhSeKjhFcF6BifvbukDEczjLT-ayCdP9ps9DnTlhK-gbHkLVLvV7f-0RPDImSViSQdr2t7PUUdU7iHH8DOBUKWDu75hn6mDNv3HfH0Wo8WabogfXGFwq-RA49PN22bDPOD7xWUim2vwecV0K9cU1_CL2OFI=w400-h79" width="400" /></a></div></div><div><br /></div><div>We just have to make every request twice. Once with a failure to get the correct cryptogram, then a second time placing the returned cryptogram in our <span style="font-family: courier;">9f26</span> field.</div></div><div><br /></div><div>Now, replay the $10 transaction, but increment the counter, and update the cryptogram, and change $10 to $100,000 in the <span style="font-family: courier;">amount</span> field.</div><div><br /></div><div><b><span style="color: red;">Error</span></b>: The server also detects this amount field being tampered with.</div><div><br /></div><div><span style="font-family: verdana;">"We become safer than Visa or MasterCard! We do not allow random merchants to modify the final amount field from the amount, signed by the chip"</span></div><div><br /></div><div>Let's try changing both the <span style="font-family: courier;">amount</span> and <span style="font-family: courier;">9f02</span> fields to $100,000 so that they match. </div><div><b><span style="color: red;"><br /></span></b></div><div><b><span style="color: red;">Error</span></b>: For that amount of money we need further verification from the client.</div><div><br /></div><div>Our tampered amount is accepted, but there's additional verification required for that size of transaction.</div><div><br /></div><div>This verification is done entirely between the card and the POS and is a simple boolean bit field when sent to the bank server. So we flip it to "verified". We claim that the POS verified the card pin by changing the <span style="font-family: courier;">9f34</span> (Cardholder Verification Method Results - CVM) field from 1f0302 to 010302.</div><div><div><br /></div><div>Changing 1f to 01 tells the server "Plaintext PIN verification performed by ICC". See the <a href="https://drive.google.com/file/d/1KMvrdTgpw22Hvdgy4D_-ks_fW3WDEwT7/preview" target="_blank">First Contact: New vulnerabilities in Contactless Payments</a> whitepaper, page 18, for full details.</div></div><div><br /></div><div><b><span style="color: red;">Error</span></b>: Not enough money in the account to charge $100,000.</div><div><br /></div><div>Ok, we can charge more than $10, but less than $100,000, let's try $10,000. </div><div><br /></div><div><span style="color: #38761d;"><b>Success</b>: </span>$10,000 spent.</div><div><br /></div><div>Now just repeat that 9 more times.</div><div><br /></div><div><span style="color: #38761d;"><b>Success</b>: </span>$100,000 spent.</div></div><div><br /></div><div><h2 style="text-align: left;">Challenge 2 - Commit other fraudulent operations</h2><div>While fuzzing various fields in the original request, an error is returned.</div><div><br /></div><div><span style="font-family: verdana;">"Transaction type should be purchase or refund."</span></div><div><br /></div><div>Alright, let's change "purchase" to "refund" in both type fields:</div><div><br /></div><div><div><span style="font-family: courier;">trans_type=EMV</span> to <span style="font-family: courier;">trans_type=refund</span></div><div><span style="font-family: inherit;">and</span></div><div><span style="font-family: courier;">type=purchase</span> to <span style="font-family: courier;">type=refund</span></div></div><div><br /></div><div>You must attempt a refund with a replay of a successful transaction (same ATC and amount) as we can only refund a charge that was made. But we can't refund immediately.</div><div><br /></div><div><b><span style="color: red;">Error</span></b>: Refund can't be issued at the moment</div><div><br /></div><div>The server has some time delay before refunds are allowed. Wait ~1 hour and try again.</div><div><br /></div><div><span style="color: #38761d;"><b>Success</b></span></div></div><div><span style="color: #38761d;"><b><br /></b></span></div><div><h2 style="text-align: left;">Challenge 3 - Steal fifteen cards from the SoftPOS</h2><div>Browsing to the directory base at www.paymentvillageprocessing.com, shows 3 other <i>.php</i> scripts.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhgCxd01yyVN8Pei2s4bBLZ_9i6F2hGp8hHXUqtQN1siliddfGajqDdJd4hqmY5tB-F0-6svMkTHUSrU3WjgY9WnGiSVJGfbVeY_5_cN_H_nCt1O0jnBzBI2gwJImmXRWW7XGTZIZANGMEwonlKIFhs1wxHcUpWJJPYrIZGjb6Act1b6zw8iW3UGtkfsg2a" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="287" data-original-width="589" height="195" src="https://blogger.googleusercontent.com/img/a/AVvXsEhgCxd01yyVN8Pei2s4bBLZ_9i6F2hGp8hHXUqtQN1siliddfGajqDdJd4hqmY5tB-F0-6svMkTHUSrU3WjgY9WnGiSVJGfbVeY_5_cN_H_nCt1O0jnBzBI2gwJImmXRWW7XGTZIZANGMEwonlKIFhs1wxHcUpWJJPYrIZGjb6Act1b6zw8iW3UGtkfsg2a=w400-h195" width="400" /></a></div></div><div><br /></div><div>Making a GET request on <i>host_reset.php</i> returns a MySQL input validation error indicating there's probably a SQL injection bug here that will allow us to dump stored card numbers. </div><div><br /></div><div><span style="font-family: verdana;">"Fatal error: Uncaught mysqli_sql_exception: Incorrect integer value: '' for column 'EUR_Trans' at row 1 in /var/www/www.paymentvillageprocessing.com/mysql.class.php:91 Stack trace: #0 /var/www/www.paymentvillageprocessing.com/mysql.class.php(91): mysqli_query() #1 /var/www/www.paymentvillageprocessing.com/host_reset.php(15): MySQL->execute() #2 {main} thrown in /var/www/www.paymentvillageprocessing.com/mysql.class.php on line 91"</span></div><div><br /></div><div>But I'm at DEF CON and I'm sitting with a lot of nice, fun, hackers in the Payment Village AND I have an intercepting proxy on my personal POS. So I just walked around and asked folks nicely if I could scan their card, while my laptop grabbed their numbers, just like a real skimmer would. 18 folks said yes.</div><div><br /></div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgmJn6DNdZo3yETN8eFAgP6hAefk6w1JV2eTRwUDKmNJMd7yDJBQQVQMeoHmhtlw33bAIDxqlVbNhXdSaEnPCa8pFsVp4CjJEua4bGz1KnbTtnG2iSq5v7xwk6EjSrZvq4ddp462UyohoI02hSQGetUyd__zGhVZkA1D4dJjzMl1l3n5Z68uHBlCKXVaVEs" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="200" data-original-width="1195" height="108" src="https://blogger.googleusercontent.com/img/a/AVvXsEgmJn6DNdZo3yETN8eFAgP6hAefk6w1JV2eTRwUDKmNJMd7yDJBQQVQMeoHmhtlw33bAIDxqlVbNhXdSaEnPCa8pFsVp4CjJEua4bGz1KnbTtnG2iSq5v7xwk6EjSrZvq4ddp462UyohoI02hSQGetUyd__zGhVZkA1D4dJjzMl1l3n5Z68uHBlCKXVaVEs=w640-h108" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Skimmed payments captured over the WiFi</td></tr></tbody></table></div><div><br /></div><div><span style="color: #38761d;"><b>Success</b></span></div><div><br /></div><div><div><span style="font-family: courier;">PAN,Date</span></div><div><span style="font-family: courier;">1016438914381100,230811</span></div><div><span style="font-family: courier;">1016438914381118,230811</span></div><div><span style="font-family: courier;">1016438914381126,230811</span></div><div><span style="font-family: courier;">1016438914381134,230811</span></div><div><span style="font-family: courier;">1016435898071137,230811</span></div><div><span style="font-family: courier;">1016439201951142,230811</span></div><div><span style="font-family: courier;">1016435898071079,230811</span></div><div><span style="font-family: courier;">1016433175481186,230811</span></div><div><span style="font-family: courier;">1016437680601097,230811</span></div><div><span style="font-family: courier;">1016435539211100,230812</span></div><div><span style="font-family: courier;">1016435656631130,230812</span></div><div><span style="font-family: courier;">1016435898071137,230812</span></div><div><span style="font-family: courier;">1016433175481038,230812</span></div><div><span style="font-family: courier;">1016437112791045,230812</span></div><div><span style="font-family: courier;">1016433175481053,230812</span></div><div><span style="font-family: courier;">1016437112791060,230812</span></div><div><span style="font-family: courier;">1016433175481186,230812</span></div><div><span style="font-family: courier;">1016435656631098,230812</span></div></div></div><div><span style="font-family: courier;"><br /></span></div><h2 style="text-align: left;">Gratitude</h2><div><div>Thank you <a href="https://twitter.com/L_AGalloway" target="_blank">Leigh-Anne</a>, <a href="https://twitter.com/a66ot" target="_blank">Tim</a>, and <a href="https://twitter.com/alecadena" target="_blank">Ali</a> for a realistic, engaging challenge. The whitepaper and write-ups from last year were very helpful to get started. And with no leaderboard, other hackers were really collaborative, and we could learn about payments together instead of competing.</div></div><div><br /></div><div>See you again next year!</div><div><br /></div><h2 style="text-align: left;">References</h2><div><ul style="text-align: left;"><li><a href="https://portswigger.net/burp/documentation/desktop/mobile/config-android-device" target="_blank">Configuring an Android device to work with Burp Suite</a></li><li><a href="https://drive.google.com/file/d/1KMvrdTgpw22Hvdgy4D_-ks_fW3WDEwT7/preview" target="_blank">First Contact: New vulnerabilities in Contactless Payments whitepaper</a></li><li><a href="https://www.paymentvillage.org/blog/how-to-solve-the-def-con-paymentvillage-org-credit-card-challenge">Payment Village 2021 Challenge Write-up</a></li><li><a href="https://emvlab.org/emvtags/" target="_blank">EMV field number to description</a></li><li><a href="https://paymentcardtools.com/emv-tag-decoders/cvm-results" target="_blank">CVM bit field decoder</a></li></ul></div>Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-81272853318750703822018-03-20T17:11:00.000-07:002018-03-20T17:31:27.079-07:00No Airline Handles Ticketing of Infants Well<div class="MsoNormal">
I've flown with my young children multiple times on both
domestic and international flights. We've flown on Alaska, Delta, American, and
Qantas. None of them have been smooth and not because of the baby.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Most airlines let you have a child on your lap, without a
booked seat, if the child is under the age of 2 during the flight. When flying
within the United States this is free, but you still have to add your child’s
name to your ticket. When flying international, the above listed carriers
require the child have a ticket, which costs 10% of the published fare, but
comes with no seat. I don't know the intricacies of this policy; I just know that
nobody seems to care about this 10% until you're about to get on the airplane.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyPOR_1UOXzMAzJx7JLAIVPc-JOI5YibnKDuSEUtrJ0mZFyBxvhR5DXQl2CgY2x7tuBEx7_vppQA07AQBhklwVchEGgfDyv8l_3TyDRATd5yBLo1OzsafpeISrMre8a65veiaSe2GNV0gc/s1600/baby_flight1.jpeg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="697" data-original-width="768" height="289" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyPOR_1UOXzMAzJx7JLAIVPc-JOI5YibnKDuSEUtrJ0mZFyBxvhR5DXQl2CgY2x7tuBEx7_vppQA07AQBhklwVchEGgfDyv8l_3TyDRATd5yBLo1OzsafpeISrMre8a65veiaSe2GNV0gc/s320/baby_flight1.jpeg" width="320" /></a>Applause to Alaska, the only carrier I’ve flown that lets
you add your infant to your ticket via their web site. All the others require
you call in and speak to an agent. As a software developer, waiting on hold for
45 minutes, being told by an automated message that I “really should use the
website” is especially frustrating. I would LOVE to use the website. Please let
me add my infant right on the booking screen instead of as a manual addendum
after the fact!</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Add in award travel and things get worse. My wife and I
recently booked an international flight with Alaska miles, where the second leg
was on American Airlines. Our infant was listed on our itinerary from the
beginning. At our layover airport, at the gate, 20 minutes before departure, with
two squirming babies in our arms, we're told we never paid the 10% infant
ticket fee. We could pay it then, plus an additional $45 “airport fee” since
we're buying a last minute ticket from a gate agent, or not get on the
airplane. Ever since airlines started charging for checked bags I often feel
like I'm being nickel and dimed, but this was full out extortion.</div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWKHIPjgo0MTqTzXwXhSYUBd2dmCsgpxyJBgFyu40qLgHc2RqT6jIy04y-fwtDPmfrjOJbqgbHUN8bTuXdzHLdLY-583kTUIpieI3mDfW3LHWxC_w_eVJBfVzDaYg8Kp45oAcyweNVLrH1/s1600/baby_flight2.jpeg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="576" data-original-width="768" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWKHIPjgo0MTqTzXwXhSYUBd2dmCsgpxyJBgFyu40qLgHc2RqT6jIy04y-fwtDPmfrjOJbqgbHUN8bTuXdzHLdLY-583kTUIpieI3mDfW3LHWxC_w_eVJBfVzDaYg8Kp45oAcyweNVLrH1/s320/baby_flight2.jpeg" width="320" /></a></div>
<div class="MsoNormal">
Even on a fairly recent domestic Alaska flight, where I was
pleasantly surprised I could add my infant to my ticket on their mobile app,
while checking in, on my way to the airport, it didn't actually work. I checked
the whole family in with the app. Then we checked our bags with an agent, went
through security, boarded the plane, and in our seats right before the airplane
door closed were asked, "Who's this baby?" Our child was not on the
flight manifest and apparently not recorded anywhere in their system. We somehow
snuck a baby on an airplane with no questions through 4 checkpoints.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
From a process perspective infants flying seems to be common
enough that most airline staff have heard of it, but not everyone knows how it
works. From a software perspective it is a special case, lower priority, less
tested, execution path. And even from the parents perspective the kids are only
under 2 for a few flights and then it's normal full fare tickets. So I somewhat
understand why ticketing my child never quite works. But still, the hardest
part about flying with an infant shouldn't be buying them a ticket.</div>
Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-54778584666510296912017-01-25T18:51:00.002-08:002017-01-25T21:08:33.508-08:00HTC 10 Won't Connect to T-Mobile International Data RoamingI bought an unlocked HTC 10 through a 3rd party vendor and have enjoyed the phone. I use T-Mobile service because of their great value and their free 2G international data roaming. Unfortunately, I ran into trouble connecting to cellular data the first time I traveled outside the USA with my new phone.<br />
<br />
After arriving in Hong Kong, I received the helpful T-Mobile text message telling me I had data roaming available:<br />
<br />
<div style="text-align: justify;">
<code>"Welcome to Hong Kong! Your T-Mobile plan gives you unlimited data at 2G speeds, calls at 20 cents/min and free texts."</code></div>
<br />
Text messages and phone calls were working, but cellular data refused to connect.<br />
<br />
Troubleshooting steps I tried:<br />
<ul>
<li>Confirmed data roaming is on</li>
<li>Put phone in airplane mode for 1 minute</li>
<li>Restarted phone</li>
<li>Tried manually connecting to every carrier. Only the automatically selected one worked</li>
<li>Enabled roaming via USSD code: #766#</li>
<li>Tried manually specifying each network type (2G, 2G/3G, 2G/3G/4G)</li>
<li>Tried my T-Mobile SIM in a different phone (worked)</li>
<li>Tried a local SIM from the roaming carrier (Hutchinson 3) in my phone (worked)</li>
<li>Tried a different T-Mobile SIM, that I know works, in my phone (didn't work)</li>
<li>Asked T-Mobile if there was any block on my number for international roaming (nope)</li>
</ul>
Much of this was suggested by the very helpful T-Mobile support team and though we whittled down the problem to something specific with my phone, they never determined the exact issue. Finally, HTC customer support suggested I check my APN settings. Sure enough, they were different between my 2nd working phone and my HTC 10. <br />
<br />
There's a <a href="https://support.t-mobile.com/docs/DOC-16171" target="_blank">known issue with the LG G3 phone</a><known g3="" issue="" lg="" phone="" the="" with=""> not having and not automatically downloading the correct APN settings to allow T-Mobile international roaming to work. Apparently, this is also true with my HTC 10 phone, though no amount of Googling mentions it.</known><br />
<br />
<known g3="" issue="" lg="" phone="" the="" with="">I believe the HTC 10 is supposed to read its APN settings from the SIM card, but instead, when a new SIM card is inserted, it prompts the user for the carrier associated with this SIM. From this user selection it populates the APNs. It appears, this hard coded list is outdated, even after an update to Android 7.0.</known><br />
<known g3="" issue="" lg="" phone="" the="" with=""><br /></known>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsw6vURPGGmu8WeA6MrdJ0R33NLpZ_hk0vg5YHMTWrSAdgIUFuyTul-1v6EAk_Z8v6CCRpvvgHhvKGshqS4WSIzD6N3lYuX5l0xGFiAFxrXTLBcDKpsYj8wXMvcMRedeQk25jI9A5NQ-lP/s1600/Screenshot_20170126-123638.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsw6vURPGGmu8WeA6MrdJ0R33NLpZ_hk0vg5YHMTWrSAdgIUFuyTul-1v6EAk_Z8v6CCRpvvgHhvKGshqS4WSIzD6N3lYuX5l0xGFiAFxrXTLBcDKpsYj8wXMvcMRedeQk25jI9A5NQ-lP/s320/Screenshot_20170126-123638.png" width="180" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRRBmy192ZDf9Mi8lgO03eNvtA0u5fqS3f9ZP9RrU7y0n-lQID31GHs__Fal-ETUqJ_5ED1ItEwFGFeCmLsmQypy4-O5lpJeRMSid4eVacYr9TxezFyAV_dVdBB6KRrZzSImNHKCVbnBuM/s1600/Screenshot_20170126-123719.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRRBmy192ZDf9Mi8lgO03eNvtA0u5fqS3f9ZP9RrU7y0n-lQID31GHs__Fal-ETUqJ_5ED1ItEwFGFeCmLsmQypy4-O5lpJeRMSid4eVacYr9TxezFyAV_dVdBB6KRrZzSImNHKCVbnBuM/s320/Screenshot_20170126-123719.png" width="180" /></a></div>
<known g3="" issue="" lg="" phone="" the="" with=""><br /></known>
<br />
With the problem finally understood, several sites mention how to manually enter an APN which enables roaming. The steps are:<br />
<ol>
<li><known g3="" issue="" lg="" phone="" the="" with="">Open <b>Settings</b> > <b>Mobile Data</b> > <b>Access point names</b></known></li>
<li><known g3="" issue="" lg="" phone="" the="" with="">Copy down the settings of the currently selected Access Point</known></li>
<li><known g3="" issue="" lg="" phone="" the="" with="">Create a new Access Point with:</known></li>
<ol>
<li><b>Name</b> = RoamingAPN</li>
<ul>
<li>This can be any name</li>
</ul>
<li><b>APN</b> = epc.tmobile.com</li>
<li><b>APN protocol</b> = IPv4</li>
<li>All other settings the same as the currently selected APN</li>
</ol>
<li>Save (with the triple dot menu)</li>
<li>Select the new "RoamingAPN"</li>
</ol>
My HTC 10 connected within 15 seconds and displayed the <b>H</b> icon in the notification tray. Though, you may need to reboot after making this change.<br />
<br />
This worked for me and hopefully will help other HTC 10 owners.<br />
<br />
<known g3="" issue="" lg="" phone="" the="" with=""><b>References</b>:</known><br />
<ul>
<li><known g3="" issue="" lg="" phone="" the="" with=""><a href="https://support.t-mobile.com/docs/DOC-16171" target="_blank">https://support.t-mobile.com/docs/DOC-16171 </a></known></li>
<li><known g3="" issue="" lg="" phone="" the="" with=""><a href="http://www.demflyers.com/2016/05/15/how-to-fix-t-mobile-international-data-roaming-on-android/" target="_blank">http://www.demflyers.com/2016/05/15/how-to-fix-t-mobile-international-data-roaming-on-android/</a></known></li>
<li><known g3="" issue="" lg="" phone="" the="" with=""><a href="http://maphappy.org/2016/05/setting-up-t-mobiles-apn-for-international-data-roaming/" target="_blank">http://maphappy.org/2016/05/setting-up-t-mobiles-apn-for-international-data-roaming/</a></known></li>
</ul>
Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com2tag:blogger.com,1999:blog-6761141672212693733.post-29510673684794808952017-01-14T18:04:00.003-08:002017-01-15T15:06:17.148-08:00Sense Home Energy Monitor App Won't ConnectI'm very excited to learn about my personal electricity usage with my <a href="https://sense.com/" target="_blank">Sense Home Energy Monitor</a>, now that it's working, but installation did not go smoothly.<br />
<br />
<h2>
The Story</h2>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpZydvLoMJQTxHlbdIK-WlIS9Zq3ABrR4OaQIB4XFgOvFuyS24XnhGIMbpdb3KIoD4xqgvqEn2Ybt2oSYV2L70VlWH5TFRV911HGEyS8TCfHjncpLstBAzed3QqBHueX3mWbDRafFbBDCl/s1600/IMAG0913.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpZydvLoMJQTxHlbdIK-WlIS9Zq3ABrR4OaQIB4XFgOvFuyS24XnhGIMbpdb3KIoD4xqgvqEn2Ybt2oSYV2L70VlWH5TFRV911HGEyS8TCfHjncpLstBAzed3QqBHueX3mWbDRafFbBDCl/s320/IMAG0913.jpg" width="240" /></a><br />
I physically installed the device in my electric panel as described in the <a href="https://sense.com/help/installguide.pdf" target="_blank">install guide</a>:<br />
<ol>
<li>Connect the WiFi antenna external to the box</li>
<li>Plug in the black and red wires to a 20A, 240V breaker</li>
<li>Clip the two current sensors around my mains </li>
</ol>
I turned on the breaker, got a happy little chime from the orange box after about 20 seconds, and saw an internal light blinking. Easy.<br />
<br />
Unfortunately, using the app to make an initial connection is where the frustration began.<br />
<br />
The short story is that the device refused to connect to my WiFi router,
until after it upgraded its software version, which it does over the
WiFi connection... Catch 22. But figuring out this problem was painful,
due to a highly simplified phone app, almost no other physical
interface on the device, and very slow tech support.<br />
<br />
First try, Android app continually fails at "Verifying Install":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLfBP_AUvcfNqP450ZRKZupIQRxwYxkVpXCQfLWzaltvNCnyeMjVO3j6nTi8kxiVvqzJpXC-D8hGQefYTE9jtqEhwnbaB2eYJ-FivK3VwTfKfspaLMnZWcEFywWFj9Qz-2NK29CcpPE-Dv/s1600/Screenshot_20170108-142644.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLfBP_AUvcfNqP450ZRKZupIQRxwYxkVpXCQfLWzaltvNCnyeMjVO3j6nTi8kxiVvqzJpXC-D8hGQefYTE9jtqEhwnbaB2eYJ-FivK3VwTfKfspaLMnZWcEFywWFj9Qz-2NK29CcpPE-Dv/s320/Screenshot_20170108-142644.jpg" width="180" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Second try, iPad app continually fails at "Registering":</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ezV7K5oHVTg4QckjivM9lLWIu-CI5-Sbj8AUkgzdeIODaxnQ0xT2Ks_XxCCvdKoyDUSn0Hm-mEmWUyXXCxX6NoDTOEaxMF4C4bI9h9ldrLWKYaINkWhn5_PSit8H137nov7doyGuHPDx/s1600/IMAG0907.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ezV7K5oHVTg4QckjivM9lLWIu-CI5-Sbj8AUkgzdeIODaxnQ0xT2Ks_XxCCvdKoyDUSn0Hm-mEmWUyXXCxX6NoDTOEaxMF4C4bI9h9ldrLWKYaINkWhn5_PSit8H137nov7doyGuHPDx/s400/IMAG0907.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Both apps after failing in two different spots, with no helpful error messages, gave a page that says "Try Again".<br />
<br />
I
tried this about 15 times, power cycling the device each time, until
finally it worked from the iPad. I made no changes of any kind between
the failures, except trying again. So it's still a mystery why the 15th
time WiFi connected. I was able to create a log in, get the app past
initialization, and then I'm given this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVT3J4QwB5RdgdKjvSDdZOf0-5AIMikRXp1ZABErsAAfUM1DR2OOEveBPd0ty1e35IfTD_FukOLgvNfAiLq6XMIzcFLDL4DFr6SkEepOs3hE7_mIvJZDipmSNOpHKAVtMUb0jsix5IO08A/s1600/Screenshot_20170110-205303.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVT3J4QwB5RdgdKjvSDdZOf0-5AIMikRXp1ZABErsAAfUM1DR2OOEveBPd0ty1e35IfTD_FukOLgvNfAiLq6XMIzcFLDL4DFr6SkEepOs3hE7_mIvJZDipmSNOpHKAVtMUb0jsix5IO08A/s320/Screenshot_20170110-205303.jpg" width="180" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
The device is "Offline".<br />
<br />
So it managed to join my WiFi network, but can't send any packets (which I confirmed from the router). More power cycling, and switching to 2 different WiFi networks, and still offline. Thankfully, a friend also has a working Sense, showed me his app, and we noticed that my device software version was older than his.<br />
<br />
<b>Mine:</b> 1.0.1277-be639d4-master<br />
<b>His:</b> 1.1.1353-d08ad0f-master<br />
<br />
So guessing/hoping that "fixed in the next version" was true, I turned on tethering on my phone, and tried connecting the device to it from my iPad. This worked and he device got online. Then I left my phone sitting next to my electrical panel, checking back periodically, if it had auto-updated itself. There is no "Update Software" option in the app. After 24 hours and 82MB of data, my device now showed version 1.1.135. Switching back to my normal WiFi network... it still didn't work! But remembering the first rule of Windows tech support, I turned it off then back on again, and hallelujah it showed "Online" and connected to my home network.<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhbX5jjUeiWkg9nu-n-q0b_9Yfmze_5qPI_H56FjhbcqrFqr-zOWNUHP869JVwIgKfB87cUCnKocIxHjOy9oZwAwsjGjBYunrmpoTdsPTOyQzSVRq7OWuFTtdnZfii5IV5Ch6h3BI-ZeCW/s1600/Screenshot_20170112-231033.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhbX5jjUeiWkg9nu-n-q0b_9Yfmze_5qPI_H56FjhbcqrFqr-zOWNUHP869JVwIgKfB87cUCnKocIxHjOy9oZwAwsjGjBYunrmpoTdsPTOyQzSVRq7OWuFTtdnZfii5IV5Ch6h3BI-ZeCW/s320/Screenshot_20170112-231033.jpg" width="180" /></a></div>
<br />
So after 4 days of troubleshooting, my Sense is now happily measuring and learning my energy usage. We'll see what insights it comes up with over the next few weeks.<br />
<br />
<h2>
Troubleshooting</h2>
<br />
If you're having a problem getting your Sense to initially register, or after registration connect to your WiFi network, here are some tips:<br />
<ol>
<li>Try setup with both the Android and iOS app. Though they look the same, they obviously work a little differently with the iOS app being more mature it appears.</li>
<li>Set up a separate WiFi network, with a different physical router. Tethering my phone worked best for me. The device likes some WiFi access points and not others. </li>
<li>Once connected to a temporary WiFi network, leave the device alone for 24 hours to download updates. There is no way to force an update, you just have to wait.</li>
<li>After software update, turn it off and back on, and reconnect to your original home network. </li>
</ol>
<br />
<h2>
Closing Thoughts</h2>
<br />
I contacted Sense customer support while having this issue and while they were very courteous and helpful with basic debugging steps, they were not very responsive. Support is only available over email, no phone, and it took 48 hours for my ticket to even be answered initially. I'm guessing the small team is overwhelmed with recent publicity.<br />
<br />
Here's the start of my feature request list for the device:<br />
<ul>
<li>Better/any error information in the app when something fails. "Can't connect to WiFi" would've been a lot more helpful than "Try Again".</li>
<li>There's no "Upgrade Now" button in the app. Having one would've saved me 24 hours.</li>
<li>There's no hard reset, or even factory reset option in the software.
Having this *may* have helped when switching from Android to iOS setup.</li>
</ul>
Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com6tag:blogger.com,1999:blog-6761141672212693733.post-47258052285667008132016-10-02T12:22:00.000-07:002016-10-02T12:23:35.412-07:00TUM CTF 2016 - totp writeup<b>Challenge Name</b>: totp<br />
<b>Point Value</b>: 100 Base + 100 Bonus<br />
<b>Description</b>: It’s time to thank Google™ for making the Internet more secure for all of us.<br />
<b>Tags</b>: web<br />
<br />
The challenge is a website with a personal diary and user database. We can create accounts and post messages to our diary. A public Users page is listed, which shows the admin email address. It's likely we need to login as an admin account and view their personal diary to find the flag.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7c-SCWfxUaKq0q4JQ0P4n7JllK4kn44tWLBMvi9Us6GfqQO9ahF1xqfUfLJ3j4Gbf1Hz9poNblnA4hwlrtVpXNs1_TbPoc61Djh2VRoiP0M0vUcBRzzk6UL44Vz5vCu7JNohIpIeTXokr/s1600/screenshot_users.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="500" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7c-SCWfxUaKq0q4JQ0P4n7JllK4kn44tWLBMvi9Us6GfqQO9ahF1xqfUfLJ3j4Gbf1Hz9poNblnA4hwlrtVpXNs1_TbPoc61Djh2VRoiP0M0vUcBRzzk6UL44Vz5vCu7JNohIpIeTXokr/s640/screenshot_users.png" width="640" /></a></div>
<br />
<br />
The catch is that account logins use a Google Authenticator time-based one-time password (TOTP). Usually, these are used in conjunction with a user selected password, but this site uses only the TOTP for login.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrNL306-WJmJHmOgoHg9zQ2Z9WMeFfSSobfWBvkR7CSe9T1T0fbWckJt8jKKxS0JSj7-Ff9Vm6zUdUhdUME_5Cy4gdNXnwy8byCqsGUfSnogQhmnDew1ScqShJUTLk7NH9G5H5-q7VB10y/s1600/screenshot_registration_form.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="499" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrNL306-WJmJHmOgoHg9zQ2Z9WMeFfSSobfWBvkR7CSe9T1T0fbWckJt8jKKxS0JSj7-Ff9Vm6zUdUhdUME_5Cy4gdNXnwy8byCqsGUfSnogQhmnDew1ScqShJUTLk7NH9G5H5-q7VB10y/s640/screenshot_registration_form.png" width="640" /></a></div>
<br />
The <a href="https://en.wikipedia.org/wiki/Google_Authenticator" target="_blank">Google Authenticator Wikipedia article</a>, provided in the challenge description, explains how TOTP generates a secret key (displayed in the 2D barcode) at first creation, and that key is used along with the current time to generate 6 digit codes using HMAC every 30 seconds. Looking at the source for the registration page, we see an example of one of these keys.<br />
<br />
<pre style="background: #ffffff; color: black;"><span style="color: #a65700;"><</span><span style="color: maroon; font-weight: bold;">div</span><span style="color: #274796;"> </span><span style="color: #074726;">class</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"col-sm-9"</span><span style="color: #a65700;">></span>
<span style="color: #a65700;"><</span><span style="color: maroon; font-weight: bold;">img</span><span style="color: #274796;"> </span><span style="color: #074726;">src</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=otpauth://totp/dairy?secret=6PP3WKGNATQYEV2V5ELFQPMD&choe=UTF-8"</span><span style="color: #a65700;">></span>
<span style="color: #a65700;"><</span><span style="color: maroon; font-weight: bold;">input</span><span style="color: #274796;"> </span><span style="color: #074726;">type</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"hidden"</span><span style="color: #274796;"> </span><span style="color: #074726;">name</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"secret"</span><span style="color: #274796;"> </span><span style="color: #074726;">value</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"6PP3WKGNATQYEV2V5ELFQPMD"</span><span style="color: #a65700;">></span>
<span style="color: #a65700;"><</span><span style="color: maroon; font-weight: bold;">input</span><span style="color: #274796;"> </span><span style="color: #074726;">type</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"password"</span><span style="color: #274796;"> </span><span style="color: #074726;">id</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"password"</span><span style="color: #274796;"> </span><span style="color: #074726;">placeholder</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"One Time Password"</span><span style="color: #274796;"> password</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"password"</span><span style="color: #274796;"> </span><span style="color: #074726;">name</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"otp"</span><span style="color: #274796;"> </span><span style="color: #074726;">required</span><span style="color: #274796;"> </span><span style="color: #074726;">class</span><span style="color: #808030;">=</span><span style="color: #0000e6;">"form-control"</span><span style="color: #a65700;">></span>
<span style="color: #a65700;"></</span><span style="color: maroon; font-weight: bold;">div</span><span style="color: #a65700;">></span></pre>
<br />
Though a Google API is generating the barcode, the 120-bit (base32) secret key is being generated by the challenge site. If we can guess what secret key was generated when the admin account was created, then we can create TOTP codes based off the current UTC time and log in as admin. I believe this was the intent of the challenge designers, as they've conspicuously provided the time the admin account was created on the Users page.<br />
<br />
<b>Registered at: </b>2015-11-28 21:21<br />
<br />
The key we eventually recover also gives a hint that a poor random number was used. Without source code, there is still a lot of guessing involved in how the random numbers are generated by the site. So while I continued to investigate that, I also setup a brute force password checker to run in the background, and that found the answer.<br />
<br />
<h3>
Brute Force</h3>
<div>
<br />
The TOTP number is 6 numeric digits, which gives 10^6 possible combinations, or 1,000,000. Not a difficult brute force attack. However, the correct number the server will accept changes ever 30 seconds. But since that number is based off a SHA-1 HMAC, it has an even distribution. Each number in that 1,000,000 is just as likely to be correct for 30 seconds as any other number. Let's say we can check 10,000 of the 1,000,000 keys within every 30 second period. If we check the same 10,000 passwords every time (say 000 000 to 010 000) then after 50 attempts (1,000,000 / 10,000 / 2) we should have guessed the right password. That's 50 attempts * 30 seconds, or 25 minutes, which is not long at all for an online brute force attack.</div>
<div>
<br /></div>
<div>
We could script this, but Burp Intruder has most of the tools we need already. First, I benchmarked how fast Burp could send login requests from my connection to the web server. I settled on 25 threads, which on my Internet connection, over 30 seconds could make about 3000 attempts. Meaning an attack time closer to 84 minutes. Next, it's not easy to script Burp to restart an Intruder attack every 30 seconds, exactly on the :00 and :30 marks. So, I went the even lazier approach of trying all 1,000,000 passwords in a row. But since the password the server picks is an even distribution, this shouldn't hurt our attack time too much in practice.</div>
<div>
<br /></div>
<div>
Lastly, playing with the site while using Burp Proxy showed no difference in the response from the "POST /?target=login.php" message on an unsuccessful login vs a successful login. Instead, the PHPSESSID returned in the cookie, has server side state, which marks the login as successful. So we'd need to do an extra "GET /" request after every POST to check for success. Well that would slow down the attack another 2x, so instead we'll check manually every 30 seconds or so.</div>
<div>
<br /></div>
<div>
So the simplified steps for the brute force attack are:</div>
<div>
<br /></div>
<div>
<b>1) Generate a file of every possible TOTP possibility:</b></div>
<div>
<br /></div>
<div>
<div style="background-color: black;">
<span style="color: #f4f4f4; font-family: "courier new" , "courier" , monospace;">$ for i in {0..999999}; do printf "%06d\n" $i >> totp_999999.txt; done</span></div>
</div>
<br />
<b>2) Start Burp Intruder with POST login requests against the web server using the generated file:</b><br />
<b><br /></b>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMERgvM3JlYIZxNYwkIUa3HnHtRBV5YoiiSbMI6qZ77kJoCekX7QyNw2gLV1UBY_f5ffPF08KAv0TOFELhZD0dr4HVDNwgSnU9rSA3fuuc4dISMfGWuegfQ-aqKG38Is2gx5IqkCGKyTP0/s1600/screenshot_burp_intruder.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="377" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMERgvM3JlYIZxNYwkIUa3HnHtRBV5YoiiSbMI6qZ77kJoCekX7QyNw2gLV1UBY_f5ffPF08KAv0TOFELhZD0dr4HVDNwgSnU9rSA3fuuc4dISMfGWuegfQ-aqKG38Is2gx5IqkCGKyTP0/s640/screenshot_burp_intruder.png" width="640" /></a></div>
<b><br /></b>
<b>3) Periodically refresh your web browser using the same PHPSESSID cookie as Burp:</b><br />
<b><br /></b>
I got lucky after about 75,000 request / 13 minutes.<br />
<b><br /></b>
<b>4) Collect the flag:</b><br />
<b><br /></b>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYT_0stMcj8kNnA31pNTHlF9DvHJORdSV597ZwNvyB9O1Qmt4eFkPBJEYe9C7kYV1X8wc4Cg1-xT6nieeKfKZSScOA7ofVwKLoBKoQ_PSDixgzCOPfx9lxvWmEif08GEPL701wa4_VXZOr/s1600/screenshot_admin_diary.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="448" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYT_0stMcj8kNnA31pNTHlF9DvHJORdSV597ZwNvyB9O1Qmt4eFkPBJEYe9C7kYV1X8wc4Cg1-xT6nieeKfKZSScOA7ofVwKLoBKoQ_PSDixgzCOPfx9lxvWmEif08GEPL701wa4_VXZOr/s640/screenshot_admin_diary.png" width="640" /></a></div>
<b><br /></b>
<b><br /></b>Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-71513232046003701712016-08-07T20:19:00.000-07:002016-08-07T20:19:06.850-07:00OpenCTF 2016 - diskomatic writeup<b>Challenge Name</b>: diskomatic<br />
<b>Point Value</b>: 200<br />
<b>Description</b>: It slices, it dices, it juliennes fries!<br />
<b>Tags</b>: forensics<br />
<br />
The challenge is a binary file with a forensics tags, so I assumed it would be pulling some data out of some other file format.<br />
<br />
<h3>
Step 1: Unpack</h3>
<h3>
</h3>
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ file diskomatic-98757b0043b8aea77e9014cff8ead7b1 <br />
diskomatic-98757b0043b8aea77e9014cff8ead7b1:
gzip compressed data, last modified: Mon Jun 22 00:08:37 2015, max
compression, from Unix</div>
<br />
Simple enough.<br />
<br />
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ zcat diskomatic-98757b0043b8aea77e9014cff8ead7b1 > diskomatic.dat</div>
<br />
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ file diskomatic.dat diskomatic.dat:
DOS/MBR boot sector, code offset 0x58+2, OEM-ID "mkdosfs", FAT 1,
Media descriptor 0xf8, sectors/track 32, heads 64, sectors 250368
(volumes > 32 MB) , FAT (32 bit), sectors/FAT 1941, serial number
0x20550574, label: "DISKOMATIC "</div>
<h3>
</h3>
<h3>
Step 2: Mount Filesystem</h3>
<h3>
</h3>
So we have a FAT filesystem image. Let's try mounting it and seeing what's inside.<br />
<br />
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ sudo mount -t vfat ./diskomatic.dat ~/mnt/dos/<br />
$ ls -la ~/mnt/dos<br />
total 5<br />
drwxr-xr-x 2 root root 512 Dec 31 1969 .<br />
drwxr-xr-x 3 grind grind 4096 Aug 6 12:41 ..<br />
$ df -h | grep dos<br />
/dev/loop0 122M 512 122M 1% /home/grind/mnt/dos<br />
$ sudo umount ~/mnt/dos</div>
<br />
Interesting so from the filesystem's perspective there are no valid files, but we have 122MB of something.<br />
<h3>
</h3>
<h3>
Step 3: Analyze the Binary</h3>
<h3>
</h3>
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ binwalk -e diskomatic.dat<br />
<br />
DECIMAL HEXADECIMAL DESCRIPTION<br />
--------------------------------------------------------------------------------<br />
17787904 0x10F6C00 PNG image, 1600 x 900, 8-bit/color RGB, non-interlaced<br />
34088942 0x20827EE Minix filesystem, V1, little endian, 30 char names, 23429 zones</div>
<br />
binwalk found a PNG file, but was unable to extract it. Interesting... perhaps it is corrupted somehow. I don't know where the Minix FS detection came from, but it's a false positive.<br />
<br />
So let's open up the file in a hex editor and take a look at location <span style="font-family: "courier new" , "courier" , monospace;">0x10F6C00</span>.<br />
<h3>
</h3>
<h3>
Step 4: Hex Editor</h3>
<h3>
<screenshot: okteta_start.png=""></screenshot:><br /><screenshot: okteta_start.png=""></screenshot:></h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbdr1xdDLkt6PWGYR4qsWZugtmEBSK28zOVhLwD9MY4MvkrYM-cguInbQ9nt0-QLu5TLXbmpa2-1F1s0p4CQZ76LoRuyjrEuhkpdbUcUwfuugs6MwY3hRXsXfLyEYDvGdUSSXb0qKcxgnl/s1600/okteta_start.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="536" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbdr1xdDLkt6PWGYR4qsWZugtmEBSK28zOVhLwD9MY4MvkrYM-cguInbQ9nt0-QLu5TLXbmpa2-1F1s0p4CQZ76LoRuyjrEuhkpdbUcUwfuugs6MwY3hRXsXfLyEYDvGdUSSXb0qKcxgnl/s640/okteta_start.png" width="640" /></a></div>
<br />
<screenshot: okteta_start.png=""></screenshot:>The beginning of the file is as `file` advertised, a FAT32 partition table. Let's go to location <span style="font-family: "courier new" , "courier" , monospace;">0x10F6C00</span>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwnHcgGaoMgWVTW9SRGAG_J26ecayGz1HYyuNTSdSzT8Nqoiio0mpAtj1rs2KXsKJWRfhh8aHEp_3HDiCEd5fPqvYgfK582bCn2GHu0wCu6vr7yucvatxtrAwD3TOJh3Ns4sLSPfMSEIWj/s1600/okteta_part0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwnHcgGaoMgWVTW9SRGAG_J26ecayGz1HYyuNTSdSzT8Nqoiio0mpAtj1rs2KXsKJWRfhh8aHEp_3HDiCEd5fPqvYgfK582bCn2GHu0wCu6vr7yucvatxtrAwD3TOJh3Ns4sLSPfMSEIWj/s640/okteta_part0.png" width="609" /></a></div>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""> </screenshot:></screenshot:><br />
Here's the PNG header, with the first IDAT section but it is noticeably
quite short. Searching for IDAT we find many more of these sections.
Each one is exactly 512B, which I believe is the block size for FAT32.
So it seems we have a PNG file which has been fragmented and we need to
piece it back together. Well let's try the naieve approach, and just
concatenate all the chunks. We can do this from within the hex editor
by cropping the file from location <span style="font-family: "courier new" , "courier" , monospace;">0x10F6C00</span> onward and then doing a find and replace on all the <span style="font-family: "courier new" , "courier" , monospace;">\x00\x00\x00\x00</span> words with nothing.<br />
<br />
We'll save this file as <i>diskomatic.png</i> and open it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxcurTkebPch0txKSJLZQDlbMBxrCV8I_Zjho-fpIMhI7HGlmBu803VOmcsSiN5O17cElxUsNPZiMv-sShnxFIYoAa_7iMWtBwJ0wTwuPW9qKVgLcQH102_p89KTodI_Xobaz0G1f3VTYF/s1600/png_crc_error.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="362" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxcurTkebPch0txKSJLZQDlbMBxrCV8I_Zjho-fpIMhI7HGlmBu803VOmcsSiN5O17cElxUsNPZiMv-sShnxFIYoAa_7iMWtBwJ0wTwuPW9qKVgLcQH102_p89KTodI_Xobaz0G1f3VTYF/s400/png_crc_error.png" width="400" /></a></div>
<br />
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png="">Well that didn't work. But the CRC error is a great hint. PNG is a simple format with chunks split into just 4 fields.</screenshot:></screenshot:></screenshot:><br />
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><br /><table border="1" style="text-align: center;">
<tbody>
<tr>
<th style="width: 8em;">Length</th>
<th style="width: 8em;">Chunk type</th>
<th style="width: 16em;">Chunk data</th>
<th style="width: 8em;">CRC</th>
</tr>
<tr>
<td>4 bytes</td>
<td>4 bytes</td>
<td><i>Length</i> bytes</td>
<td>4 bytes</td>
</tr>
</tbody></table>
</screenshot:></screenshot:></screenshot:><br />
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png="">But you don't have to take my word for it, you can read the <wiki><a href="https://en.wikipedia.org/wiki/Portable_Network_Graphics#Technical_details" target="_blank">Wikipedia article</a>.<br /><br />Looking at the 512B blocks in the hex editor again, we see that the PNG chunks are split in the middle of the data section, leaving some data and the CRC for that data at the front of the next block. So we have a fragmented file, but we have a method of determining in which order the pieces are supposed to fit back together by combining two blocks and then checking whether the CRC for the PNG chunk is correct. So now we have an idea of the problem and a solution, but first we'll need to extract each block of the PNG file.</wiki></screenshot:></screenshot:></screenshot:><br />
<h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki> </wiki></screenshot:></screenshot:></screenshot:></h3>
<h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki>Step 5: Programmatically Extracting FAT32 Blocks</wiki></screenshot:></screenshot:></screenshot:></h3>
<h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki> </wiki></screenshot:></screenshot:></screenshot:></h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki>At first I just selected a few blocks in the hex editor and saved them as new files. But after about 6 of these, with the location not moving much, I realized I'd probably need to automate this.</wiki></screenshot:></screenshot:></screenshot:><br />
<br />
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ grep -abc IDAT diskomatic.dat<br />
4976</div>
<br />
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki>Yep, I definitely don't want to copy'n'paste 5000 times by hand. So I wrote some Python:</wiki></screenshot:></screenshot:></screenshot:><br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #400000;">FILE</span> <span style="color: #308080;">=</span> <span style="color: #1060b6;">"diskomatic.dat"</span>
<span style="color: #200080; font-weight: bold;">def</span> get_parts<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">:</span>
fh <span style="color: #308080;">=</span> <span style="color: #400000;">open</span><span style="color: #308080;">(</span><span style="color: #400000;">FILE</span><span style="color: #308080;">,</span> <span style="color: #1060b6;">'rb'</span><span style="color: #308080;">)</span>
parts <span style="color: #308080;">=</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #595979;"># we know the location of the first part from `binwalk`</span>
fh<span style="color: #308080;">.</span>seek<span style="color: #308080;">(</span><span style="color: #008c00;">0x010F6C00</span><span style="color: #308080;">)</span>
p_idx <span style="color: #308080;">=</span> <span style="color: #008c00;">0</span>
part <span style="color: #308080;">=</span> <span style="color: #1060b6;">""</span>
<span style="color: #200080; font-weight: bold;">try</span><span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">while</span> <span style="color: #074726;">True</span><span style="color: #308080;">:</span>
block <span style="color: #308080;">=</span> fh<span style="color: #308080;">.</span>read<span style="color: #308080;">(</span><span style="color: #008c00;">512</span><span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">if</span> <span style="color: #400000;">len</span><span style="color: #308080;">(</span>block<span style="color: #308080;">)</span> <span style="color: #44aadd;">==</span> <span style="color: #008c00;">0</span><span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">break</span>
<span style="color: #200080; font-weight: bold;">if</span> block<span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">:</span><span style="color: #008c00;">4</span><span style="color: #308080;">]</span> <span style="color: #44aadd;">!=</span> <span style="color: #1060b6;">"</span><span style="color: #0f69ff;">\x</span><span style="color: #1060b6;">00</span><span style="color: #0f69ff;">\x</span><span style="color: #1060b6;">00</span><span style="color: #0f69ff;">\x</span><span style="color: #1060b6;">00</span><span style="color: #0f69ff;">\x</span><span style="color: #1060b6;">00"</span><span style="color: #308080;">:</span>
fh2 <span style="color: #308080;">=</span> <span style="color: #400000;">open</span><span style="color: #308080;">(</span><span style="color: #1060b6;">"parts/p"</span><span style="color: #44aadd;">+</span><span style="color: #400000;">str</span><span style="color: #308080;">(</span>p_idx<span style="color: #308080;">)</span><span style="color: #44aadd;">+</span><span style="color: #1060b6;">".png.part"</span><span style="color: #308080;">,</span> <span style="color: #1060b6;">"wb"</span><span style="color: #308080;">)</span>
fh2<span style="color: #308080;">.</span>write<span style="color: #308080;">(</span>block<span style="color: #308080;">)</span>
fh2<span style="color: #308080;">.</span>close<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
p_idx <span style="color: #44aadd;">+</span><span style="color: #308080;">=</span> <span style="color: #008c00;">1</span>
offset <span style="color: #308080;">=</span> fh<span style="color: #308080;">.</span>tell<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">if</span> offset <span style="color: #44aadd;">%</span> <span style="color: #008c00;">0x100000</span> <span style="color: #44aadd;">==</span> <span style="color: #008c00;">0</span><span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">print</span> <span style="color: #1060b6;">"0x%x"</span> <span style="color: #44aadd;">%</span> offset
<span style="color: #200080; font-weight: bold;">except</span> <span style="color: #074726;">EOFError</span><span style="color: #308080;">:</span>
fh<span style="color: #308080;">.</span>close<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
</pre>
<br />
This writes all the blocks/parts out, 1 per file, and puts them in a directory called parts. After running this code we have 6238 files.<br />
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki></wiki></screenshot:></screenshot:></screenshot:>
<br />
<div style="background-color: black; color: #f4f4f4; font-family: "Courier New",Courier,monospace;">
$ cd parts<br />
$ ls | wc<br />
6238 56144 354468<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki></wiki></screenshot:></screenshot:></screenshot:><br />
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki></wiki></screenshot:></screenshot:></screenshot:></div>
<h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki> </wiki></screenshot:></screenshot:></screenshot:></h3>
<h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki>Step 6: Reassemble the Blocks</wiki></screenshot:></screenshot:></screenshot:></h3>
<h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki> </wiki></screenshot:></screenshot:></screenshot:></h3>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki></wiki></screenshot:></screenshot:></screenshot:>First I tested my hypothesis that all the blocks we needed were present,
by programmatically concatenating every block with the first block, and
was successful finding one block.<br />
<br />
From that code, I moved on to
checking every block against every other block, and constructing the
correct ordering of blocks in a dictionary. Then writing that string of
blocks out into a single PNG file. The full code, excluding the
get_parts() function is:<br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #595979;">#!/usr/bin/env python</span>
<span style="color: #200080; font-weight: bold;">import</span> binascii
<span style="color: #200080; font-weight: bold;">import</span> struct
<span style="color: #400000;">FILE</span> <span style="color: #308080;">=</span> <span style="color: #1060b6;">"diskomatic.dat"</span>
NUM_CHUNKS <span style="color: #308080;">=</span> <span style="color: #008c00;">6238</span>
FIRST_PART <span style="color: #308080;">=</span> <span style="color: #008c00;">0</span>
<span style="color: #200080; font-weight: bold;">def</span> tokenize_chunk<span style="color: #308080;">(</span>data<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
length <span style="color: #308080;">=</span> struct<span style="color: #308080;">.</span>unpack<span style="color: #308080;">(</span><span style="color: #1060b6;">"!I"</span><span style="color: #308080;">,</span> data<span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">:</span><span style="color: #008c00;">4</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">]</span>
chunk <span style="color: #308080;">=</span> data<span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">:</span><span style="color: #008c00;">4</span><span style="color: #44aadd;">+</span><span style="color: #008c00;">4</span><span style="color: #44aadd;">+</span>length<span style="color: #44aadd;">+</span><span style="color: #008c00;">4</span><span style="color: #308080;">]</span>
<span style="color: #200080; font-weight: bold;">return</span> chunk
<span style="color: #200080; font-weight: bold;">def</span> png_chunk_verify_crc<span style="color: #308080;">(</span>chunk<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
length <span style="color: #308080;">=</span> struct<span style="color: #308080;">.</span>unpack<span style="color: #308080;">(</span><span style="color: #1060b6;">"!I"</span><span style="color: #308080;">,</span> chunk<span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">:</span><span style="color: #008c00;">4</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">]</span>
ctype <span style="color: #308080;">=</span> chunk<span style="color: #308080;">[</span><span style="color: #008c00;">4</span><span style="color: #308080;">:</span><span style="color: #008c00;">8</span><span style="color: #308080;">]</span>
data <span style="color: #308080;">=</span> chunk<span style="color: #308080;">[</span><span style="color: #008c00;">8</span><span style="color: #308080;">:</span><span style="color: #008c00;">8</span><span style="color: #44aadd;">+</span>length<span style="color: #308080;">]</span>
crc <span style="color: #308080;">=</span> struct<span style="color: #308080;">.</span>unpack<span style="color: #308080;">(</span><span style="color: #1060b6;">"!I"</span><span style="color: #308080;">,</span> chunk<span style="color: #308080;">[</span><span style="color: #008c00;">8</span><span style="color: #44aadd;">+</span>length<span style="color: #308080;">:</span><span style="color: #008c00;">8</span><span style="color: #44aadd;">+</span>length<span style="color: #44aadd;">+</span><span style="color: #008c00;">4</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">]</span>
<span style="color: #595979;"># The CRC is a network-byte-order CRC-32 computed over the chunk type and</span>
<span style="color: #595979;"># chunk data, but not the length.</span>
crc_c <span style="color: #308080;">=</span> <span style="color: #308080;">(</span>binascii<span style="color: #308080;">.</span>crc32<span style="color: #308080;">(</span>chunk<span style="color: #308080;">[</span><span style="color: #008c00;">4</span><span style="color: #308080;">:</span><span style="color: #44aadd;">-</span><span style="color: #008c00;">4</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span> <span style="color: #44aadd;">&</span>amp<span style="color: #308080;">;</span> <span style="color: #008c00;">0xFFFFFFFF</span><span style="color: #308080;">)</span>
valid <span style="color: #308080;">=</span> <span style="color: #308080;">(</span>crc <span style="color: #44aadd;">==</span> crc_c<span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">return</span> valid
<span style="color: #200080; font-weight: bold;">def</span> read_parts<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">:</span>
parts <span style="color: #308080;">=</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #595979;"># XXX: magic number: that's how many chunks were in the original file</span>
<span style="color: #200080; font-weight: bold;">for</span> i <span style="color: #200080; font-weight: bold;">in</span> <span style="color: #400000;">xrange</span><span style="color: #308080;">(</span><span style="color: #008c00;">0</span><span style="color: #308080;">,</span> NUM_CHUNKS<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
fh <span style="color: #308080;">=</span> <span style="color: #400000;">open</span><span style="color: #308080;">(</span><span style="color: #1060b6;">"parts/p"</span><span style="color: #44aadd;">+</span><span style="color: #400000;">str</span><span style="color: #308080;">(</span>i<span style="color: #308080;">)</span><span style="color: #44aadd;">+</span><span style="color: #1060b6;">".png.part"</span><span style="color: #308080;">,</span> <span style="color: #1060b6;">"rb"</span><span style="color: #308080;">)</span>
data <span style="color: #308080;">=</span> bytearray<span style="color: #308080;">(</span>fh<span style="color: #308080;">.</span>read<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
parts<span style="color: #308080;">.</span>append<span style="color: #308080;">(</span>data<span style="color: #308080;">)</span>
fh<span style="color: #308080;">.</span>close<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">return</span> parts
<span style="color: #200080; font-weight: bold;">def</span> compare_parts<span style="color: #308080;">(</span>parts<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
parts_dict <span style="color: #308080;">=</span> <span style="color: #406080;">{</span><span style="color: #406080;">}</span>
<span style="color: #200080; font-weight: bold;">for</span> i <span style="color: #200080; font-weight: bold;">in</span> <span style="color: #400000;">xrange</span><span style="color: #308080;">(</span><span style="color: #008c00;">1</span><span style="color: #308080;">,</span> NUM_CHUNKS<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
first_two <span style="color: #308080;">=</span> parts<span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">]</span> <span style="color: #44aadd;">+</span> parts<span style="color: #308080;">[</span>i<span style="color: #308080;">]</span>
<span style="color: #595979;"># XXX: magic number, for first one, because there's the PNG header</span>
chunk <span style="color: #308080;">=</span> tokenize_chunk<span style="color: #308080;">(</span>first_two<span style="color: #308080;">[</span><span style="color: #008c00;">0x2E</span><span style="color: #308080;">:</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span>
matched <span style="color: #308080;">=</span> png_chunk_verify_crc<span style="color: #308080;">(</span>chunk<span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">if</span> matched<span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">print</span> <span style="color: #1060b6;">"Chunk 0 and %d matched!"</span> <span style="color: #44aadd;">%</span> i
parts_dict<span style="color: #308080;">[</span><span style="color: #008c00;">0</span><span style="color: #308080;">]</span> <span style="color: #308080;">=</span> i
<span style="color: #200080; font-weight: bold;">break</span>
<span style="color: #200080; font-weight: bold;">for</span> i <span style="color: #200080; font-weight: bold;">in</span> <span style="color: #400000;">xrange</span><span style="color: #308080;">(</span><span style="color: #008c00;">1</span><span style="color: #308080;">,</span> NUM_CHUNKS<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
matched <span style="color: #308080;">=</span> <span style="color: #074726;">False</span>
<span style="color: #200080; font-weight: bold;">for</span> j <span style="color: #200080; font-weight: bold;">in</span> <span style="color: #400000;">xrange</span><span style="color: #308080;">(</span><span style="color: #008c00;">1</span><span style="color: #308080;">,</span> NUM_CHUNKS<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">if</span> i <span style="color: #44aadd;">==</span> j<span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">continue</span>
two_parts <span style="color: #308080;">=</span> parts<span style="color: #308080;">[</span>i<span style="color: #308080;">]</span> <span style="color: #44aadd;">+</span> parts<span style="color: #308080;">[</span>j<span style="color: #308080;">]</span>
<span style="color: #595979;"># XXX: magic number, most other parts seem to have the same offset</span>
offset_to_chunk <span style="color: #308080;">=</span> <span style="color: #008c00;">0x2E</span>
chunk <span style="color: #308080;">=</span> tokenize_chunk<span style="color: #308080;">(</span>two_parts<span style="color: #308080;">[</span>offset_to_chunk<span style="color: #308080;">:</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span> </pre>
<pre style="background: #f6f8ff; color: #000020;"> matched <span style="color: #308080;">=</span> png_chunk_verify_crc<span style="color: #308080;">(</span>chunk<span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">if</span> matched<span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">if</span> i <span style="color: #44aadd;">%</span> <span style="color: #008c00;">100</span> <span style="color: #44aadd;">==</span> <span style="color: #008c00;">0</span><span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">print</span> <span style="color: #1060b6;">"Chunk %d and %d matched!"</span> <span style="color: #44aadd;">%</span> <span style="color: #308080;">(</span>i<span style="color: #308080;">,</span> j<span style="color: #308080;">)</span>
parts_dict<span style="color: #308080;">[</span>i<span style="color: #308080;">]</span> <span style="color: #308080;">=</span> j
<span style="color: #200080; font-weight: bold;">break</span>
<span style="color: #200080; font-weight: bold;">if</span> <span style="color: #200080; font-weight: bold;">not</span> matched<span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">print</span> <span style="color: #1060b6;">"Chunk %d had no match!"</span> <span style="color: #44aadd;">%</span> <span style="color: #308080;">(</span>i<span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">return</span> parts_dict
<span style="color: #200080; font-weight: bold;">def</span> write_parts<span style="color: #308080;">(</span>parts<span style="color: #308080;">,</span> parts_dict<span style="color: #308080;">)</span><span style="color: #308080;">:</span>
fh <span style="color: #308080;">=</span> <span style="color: #400000;">open</span><span style="color: #308080;">(</span><span style="color: #1060b6;">"final.png"</span><span style="color: #308080;">,</span> <span style="color: #1060b6;">"wb"</span><span style="color: #308080;">)</span>
next_key <span style="color: #308080;">=</span> <span style="color: #008c00;">0</span>
<span style="color: #200080; font-weight: bold;">for</span> i <span style="color: #200080; font-weight: bold;">in</span> parts_dict<span style="color: #308080;">.</span>iterkeys<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">:</span>
fh<span style="color: #308080;">.</span>write<span style="color: #308080;">(</span>parts<span style="color: #308080;">[</span>next_key<span style="color: #308080;">]</span><span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">try</span><span style="color: #308080;">:</span>
next_key <span style="color: #308080;">=</span> parts_dict<span style="color: #308080;">[</span>next_key<span style="color: #308080;">]</span>
<span style="color: #200080; font-weight: bold;">except</span> <span style="color: #074726;">KeyError</span><span style="color: #308080;">:</span>
<span style="color: #200080; font-weight: bold;">print</span> <span style="color: #1060b6;">"Hit KeyError"</span>
fh<span style="color: #308080;">.</span>close<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">def</span> main<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">:</span>
parts <span style="color: #308080;">=</span> read_parts<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
parts_dict <span style="color: #308080;">=</span> compare_parts<span style="color: #308080;">(</span>parts<span style="color: #308080;">)</span>
write_parts<span style="color: #308080;">(</span>parts<span style="color: #308080;">,</span> parts_dict<span style="color: #308080;">)</span>
<span style="color: #200080; font-weight: bold;">if</span> <span style="color: #074726;">__name__</span> <span style="color: #44aadd;">==</span> <span style="color: #1060b6;">"__main__"</span><span style="color: #308080;">:</span>
main<span style="color: #308080;">(</span><span style="color: #308080;">)</span>
</pre>
<br />
The nested for loops do some unnecessary work, but the whole thing runs in under 5 minutes on my laptop.<br />
<h3>
</h3>
<h3>
Step 7: Collect the Flag</h3>
<h3>
</h3>
Now we have a (hopefully) valid PNG. Let's open it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZMZkX6bR53kwfPEwBbKZppgcwtbmCSrXLD45dj9clLYP0U0Jx4p9msZMy-PZyIMEADp4WnR-h-n7kKPSpk4sgEbg9AUbfNov2Z6wqezLQOufGa89sOddjEVYjLVAxpTbZgBg_rH_it5Wk/s1600/final.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZMZkX6bR53kwfPEwBbKZppgcwtbmCSrXLD45dj9clLYP0U0Jx4p9msZMy-PZyIMEADp4WnR-h-n7kKPSpk4sgEbg9AUbfNov2Z6wqezLQOufGa89sOddjEVYjLVAxpTbZgBg_rH_it5Wk/s640/final.png" width="640" /></a></div>
<screenshot: okteta_start.png=""><screenshot: okteta_part0.png=""><screenshot: png_crc_error.png=""><wiki><screenshot: final.png=""><br /></screenshot:></wiki></screenshot:></screenshot:></screenshot:>
<!--Syntax highlighter: https://tohtml.com/python/-->Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com3tag:blogger.com,1999:blog-6761141672212693733.post-18246530857724008742016-06-03T23:12:00.000-07:002016-06-03T23:16:21.871-07:00Running IDA Evaluation Version on Kali Linux 2016.1 64-bitThe <a href="https://www.hex-rays.com/products/ida/support/download_demo.shtml" target="_blank">IDA Evaluation Version</a> previously came installed with Kali Linux 1.0, but since the upgrade to 2.0 and now Rolling Edition, IDA is no longer present. Since the evaluation version is available as 32-bit binaries only, getting it running requires figuring out the rather large set of dependent 32-bit libraries that must be installed on Kali 64-bit.<br />
<br />
For a quick fix, run the following commands:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">
$ sudo dpkg --add-architecture i386<br />
$ sudo apt-get update<br />
$ sudo apt-get install libglib2.0-0:i386 libx11-xcb1:i386 libxi6:i386 libsm6:i386 libfontconfig1:i386 libqt5gui5:i386
</div>
<br />
Now IDA should successfully execute from the CLI and give you a graphical window to accept the IDA License Agreement.<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">
<span style="color: #6fa8dc;">~/Downloads/idademo69</span>$ ./idaq
</div>
<br />
How to determine what libraries are missing and which packages provide them involves the iterative process of:<br />
<ol>
<li>Check for missing shared objects</li>
<li>Check which package provides them</li>
<li>Install that package </li>
</ol>
For example:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">
<span style="color: #6fa8dc;">~/Downloads/idademo69</span>$ ldd idaq | grep "not found"<br />
libgobject-2.0.so.0 => not found<br />
libgthread-2.0.so.0 => not found<br />
libglib-2.0.so.0 => not found<br />
libXext.so.6 => not found<br />
libX11.so.6 => not found<br />
libgthread-2.0.so.0 => not found<br />
libglib-2.0.so.0 => not found<br />
<span style="color: #6fa8dc;">~/Downloads/idademo69</span>$ dpkg -S libXext.so.6<br />
libxext6:amd64: /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0<br />
libxext6:amd64: /usr/lib/x86_64-linux-gnu/libXext.so.6<br />
<span style="color: #6fa8dc;">~/Downloads/idademo69</span>$ sudo apt-get install libxext6:i386</div>
<h4>
</h4>
<h4>
References</h4>
<ul>
<li><a href="http://codewriteup.blogspot.com/2014/02/install-ida-debugger-on-ubuntu-1310-64.html">http://codewriteup.blogspot.com/2014/02/install-ida-debugger-on-ubuntu-1310-64.html</a></li>
<li><a href="http://gruba.blogspot.com/2012/05/ida-pro-62-ubuntu-1204-amd64.html">http://gruba.blogspot.com/2012/05/ida-pro-62-ubuntu-1204-amd64.html</a></li>
</ul>
Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-7302593269498076022015-03-28T21:26:00.000-07:002015-03-28T21:26:15.599-07:0032-bit to 64-bit Conversion Woes with strtoul() Proper error checking when using the strtol() family of functions is notoriously difficult. See this <a href="http://stackoverflow.com/questions/14176123/correct-usage-of-strtol" target="_blank">stackoverflow thread</a> explaining it.<br />
<br />
Focusing only on strtoul() here, the correct error checking in most cases is:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<table><tbody>
<tr><td><pre style="line-height: 125%; margin: 0;"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17</pre>
</td><td><pre style="line-height: 125%; margin: 0;"><span style="color: #333399; font-weight: bold;">unsigned</span> <span style="color: #333399; font-weight: bold;">long</span>
<span style="color: #0066bb; font-weight: bold;">mystrtoul</span>(<span style="color: #333399; font-weight: bold;">char</span> <span style="color: #333333;">*</span>str, <span style="color: #333399; font-weight: bold;">bool</span> <span style="color: #333333;">*</span>success)
{
<span style="color: #333399; font-weight: bold;">unsigned</span> <span style="color: #333399; font-weight: bold;">long</span> val <span style="color: #333333;">=</span> <span style="color: #0000dd; font-weight: bold;">0</span>;
<span style="color: #333399; font-weight: bold;">char</span> <span style="color: #333333;">*</span>eptr <span style="color: #333333;">=</span> <span style="color: #007020;">NULL</span>;
errno <span style="color: #333333;">=</span> <span style="color: #0000dd; font-weight: bold;">0</span>;
<span style="color: #333333;">*</span>success <span style="color: #333333;">=</span> <span style="color: #007020;">true</span>;
val <span style="color: #333333;">=</span> strtoul(str, <span style="color: #333333;">&</span>eptr, <span style="color: #0000dd; font-weight: bold;">0</span>);
<span style="color: #008800; font-weight: bold;">if</span> (eptr <span style="color: #333333;">==</span> str <span style="color: #333333;">||</span> <span style="color: #333333;">*</span>eptr <span style="color: #333333;">!=</span> <span style="color: #0044dd;">'\0'</span> <span style="color: #333333;">||</span>
(val <span style="color: #333333;">==</span> ULONG_MAX <span style="color: #333333;">&&</span> errno <span style="color: #333333;">==</span> ERANGE) <span style="color: #333333;">||</span>
(val <span style="color: #333333;">==</span> <span style="color: #0000dd; font-weight: bold;">0</span> <span style="color: #333333;">&&</span> errno <span style="color: #333333;">==</span> EINVAL))
<span style="color: #333333;">*</span>success <span style="color: #333333;">=</span> <span style="color: #007020;">false</span>;
<span style="color: #008800; font-weight: bold;">return</span> val;
}
</pre>
</td></tr>
</tbody></table>
</div>
<br />
That is:<br />
<ul>
<li>IF your end pointer points to the beginning of your string, you've got a problem</li>
<li>IF your end pointer doesn't point to the end of your string, you've got a problem</li>
<li>IF your output is at the max end of the range (a potentially valid value) AND you get errno, you've got a problem</li>
<li>IF your output is 0 (a potentially valid value) AND you get errno, you've got a problem</li>
<li>ELSE congratulations, you've converted a string to an integer</li>
</ul>
Understandably, a lot of people get this error checking wrong when using this function. I've reviewed source code I work on and found mistakes. But even if you got all of this right, you may still have gotten it wrong.<br />
<br />
If you originally wrote this code for a 32-bit system, with the intention of converting and storing a 32-bit number, then what you wrote is correct. If you then recompile this correct code on a 64-bit system, it becomes incorrect.<br />
<br />
That's right with no modifications (and no compiler warnings), your carefully reviewed integer conversion function goes from right to (dangerously) wrong. <br />
<br />
For example give as input "4294967296" and assign to an unsigned int:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<table><tbody>
<tr><td><pre style="line-height: 125%; margin: 0;"> 1
2
3
4
5
6
7
8
9
10
11</pre>
</td><td><pre style="line-height: 125%; margin: 0;"><span style="color: #333399; font-weight: bold;">int</span>
<span style="color: #0066bb; font-weight: bold;">main</span>(<span style="color: #333399; font-weight: bold;">void</span>)
{
<span style="color: #333399; font-weight: bold;">bool</span> success <span style="color: #333333;">=</span> <span style="color: #007020;">false</span>;
<span style="color: #333399; font-weight: bold;">unsigned</span> <span style="color: #333399; font-weight: bold;">int</span> val <span style="color: #333333;">=</span> <span style="color: #0000dd; font-weight: bold;">0</span>;
val <span style="color: #333333;">=</span> mystrtoul(<span style="background-color: #fff0f0;">"4294967296"</span>, <span style="color: #333333;">&</span>success);
printf(<span style="background-color: #fff0f0;">"%s %u</span><span style="background-color: #fff0f0; color: #666666; font-weight: bold;">\n</span><span style="background-color: #fff0f0;">"</span>, success<span style="color: #333333;">?</span><span style="background-color: #fff0f0;">"success"</span><span style="color: #333333;">:</span><span style="background-color: #fff0f0;">"failure"</span>, val);
<span style="color: #008800; font-weight: bold;">return</span> <span style="color: #0000dd; font-weight: bold;">0</span>;
}
</pre>
</td></tr>
</tbody></table>
</div>
<br />
Result:<br />
<ul>
<li>On 32-bit Linux: failure 4294967296 (ERANGE)</li>
<li>On 64-bit Linux: success 0</li>
</ul>
The 32-bit compilation catches the integer overflow and errors out. The 64-bit version does not and rolls back around to 0. So your output is wrong and the usual possible errors and security holes with integer overflows apply:<br />
<ul>
<li>malloc(4294967296) allocate 0 bytes and a buffer overflow is created</li>
<li>uid_t 4294967296 become 0 and a user is root </li>
</ul>
And strings passed in to strtoul() are very often user controlled input. That's why you need to convert them from an on-the-wire, CLI, or on-disk string format to an in-memory integer.<br />
<br />
This isn't strictly a strtoul() problem. This is a <a href="http://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models" target="_blank">LP64 data model</a><link></link> problem. Any assumption about the 32-bit size of a long needs to be checked when converting from 32-bit to 64-bit runtime. This is just a particularly tricky example of the trade offs made by LP64.Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-14235142365114255682012-02-04T16:19:00.000-08:002012-02-04T16:19:34.679-08:00Windows Update Error Code 0x80073712I have several Windows 7 VMs which I use for various dev testing. For the past 9 months all of them having been failing during Windows Update. Most updates apply, but Service Pack 1 consistently errors during installation with the unknown error code 0x80073712.<br />
<br />
Microsoft has a well written KB explaining steps you can take when this issue occurs: <br />
<br />
<a href="http://support.microsoft.com/kb/957310" target="_blank">http://support.microsoft.com/kb/957310</a><br />
<br />
I went through each step, running the <b>checksur.exe</b> and <b>sfc.exe</b> tools and downloading the stand alone Service Pack 1 installer. All failed. Next, I tried step 4 attempting to upgrade with the original Windows 7 CD. Note, that the CD image must be the exact same used to install the original OS. Same release flavor, Enterprise, in my case, and same SP level. I thought I'd want to upgrade using the ISO image with SP1 built in, but no that refuses to even attempt an upgrade.<br />
<br />
Finally, with the correct ISO image I got the clue that no previous error message had given:<br />
<br />
<ul><li><span style="font-family: Georgia,"Times New Roman",serif;">There is not enough free space on partition (C:). A total of 16372 megabytes (MB) of free disk space is required. Try Disk Cleanup, or moving files to an external location such as a CD, DVD, or external hard drive, and then restart the installation.</span></li>
</ul><br />
I didn't have enough free space on the drive to install the Service Pack. At first I was surprised because I had 10GB free, but in fact Win7 SP1 requires about 16GB to install. Next, I was annoyed that this simple error wasn't raised by Windows Update in the first place.<br />
<br />
I keep my drive space pretty sparse on my test VMs because they don't have many applications or data stored on them. But a 40GB primary drive size is not enough for Windows 7. I expanded it to 80GB using VMWare Workstation 8's built-in "Expand Disk" tools then used <a href="http://www.sysresccd.org/" target="_blank">sysresccd</a> and gparted to expand the NTFS partition across the whole disk.<br />
<br />
Success! Windows Update installs Service Pack 1 with no problems. I hope this helps you or someone else avoid a half-day of IT induced profanity.Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-70489338748576989212012-01-20T23:32:00.000-08:002012-01-23T19:58:38.193-08:00Using shquote() to Combat Command InjectionI've been incredibly impressed with FreeBSD's <b><a href="http://www.daemon-systems.org/man/shquote.3.html" target="_blank">shquote(3)</a> </b>function as a singular way to prevent command injection in a server process. I'm currently working on a RESTful API (aren't we all), backed in part by a server daemon written in C. The largest threat to any networked API is user input. A server developer must assume that all input is malicious until proven valid. There are many types of injection attacks against web services; I've been focused on Unix command injection today.<br />
<br />
Let's say you're building a service which takes a directory path as input and creates this directory path on the server's file system. In C we can just call:<br />
<br />
<pre><a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line1">1</a> <b>void</b> <span style="color: #2040a0;">mkdir_recursive</span>(<b>char</b>* <span style="color: #2040a0;">path</span>) <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line2">2</a> <b>char</b>* <span style="color: #2040a0;">cmd</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line3">3</a> <span style="color: #2040a0;">asprintf</span>(&<span style="color: #2040a0;">cmd</span>, <span style="color: green;">"mkdir -p %s"</span>, <span style="color: #2040a0;">path</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line4">4</a> <span style="color: #2040a0;">system</span>(<span style="color: #2040a0;">cmd</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line5">5</a> <b>}</b>
</pre><br />
Now wait! Using <b>system()</b> is bad! It's prone to command injection attacks and there's usually always a programmable API to do the same thing in a safer, cleaner way.<br />
<br />
I agree, but I am interested in exploring command injection and <code>mkdir -p</code> is so convenient for recursive directory creation. There's no easy libc alternative and writing it myself would take 10x as much code. Yes, I'm complaining about 3 lines vs 30, but Larry Wall says laziness is one of a programmers greatest virtues.<br />
<br />
So normally to protect this simple code from command injection we'd validate the input for shell meta-characters. We must check for ; & ( ) ` | > < $. These could allow an attacker to append an arbitrary command onto our simple mkdir, read or write files, and access environment variables. For instance we could receive a path which would make the above command look like this:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">mkdir -p a/new/dir ; cat /etc/passwd > /usr/local/apache/htdocs/passwd</div><br />
Which unfiltered would cause the server's local user list to now be published in a publicly accessible directory. To further cause issues, in my particular use case, all of the above characters are valid for Unix directory paths. So simply removing them from the user's input would be incorrect behavior.<br />
<br />
In comes <b>shquote()</b>. It protects against all of these command injections and allows these characters to be used in path names, because it puts everything in strong quotes. With a refactor of our above code snippet:<br />
<br />
<pre><a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line1">1</a> <b>void</b> <span style="color: #2040a0;">mkdir_recursive</span>(<b>char</b>* <span style="color: #2040a0;">path</span>) <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line2">2</a> <b>char</b> *<span style="color: #2040a0;">cmd</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line3">3</a> <b>int</b> <span style="color: #2040a0;">len</span> = <span style="color: #2040a0;">shquote</span>(<span style="color: #2040a0;">path</span>, <span style="color: #2040a0;">NULL</span>, <span style="color: red;">0</span>) + <span style="color: red;">1</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line4">4</a> <b>char</b> *<span style="color: #2040a0;">safe_path</span> = <span style="color: #2040a0;">malloc</span>(<span style="color: #2040a0;">len</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line5">5</a> <span style="color: #2040a0;">shquote</span>(<span style="color: #2040a0;">path</span>, &<span style="color: #2040a0;">safe_path</span>, <span style="color: #2040a0;">len</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line6">6</a> <span style="color: #2040a0;">asprintf</span>(&<span style="color: #2040a0;">cmd</span>, <span style="color: green;">"mkdir -p %s"</span>, <span style="color: #2040a0;">path</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line7">7</a> <span style="color: #2040a0;">system</span>(<span style="color: #2040a0;">cmd</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line8">8</a> <b>}</b>
</pre><br />
The execute command now looks like this:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">mkdir -p 'a/new/dir ; cat /etc/passwd > /usr/local/apache/htdocs/passwd'</div><br />
Which will safely create 10 new directories, instead of calling 2 commands.<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;"># while [ "`ls`" ]; do ls -F && cd "`ls`"; done<br />
a/<br />
new/<br />
dir ; cat /<br />
etc/<br />
passwd > /<br />
usr/<br />
local/<br />
apache/<br />
htdocs/<br />
passwd/</div><br />
Surely we can still trick <b>shquote()</b> with embedded \ or ' characters right? Let's try adding intermediate quotes to break up the two commands:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">a/new/dir<span style="color: red;">' ; '</span>cat /etc/passwd > /usr/local/apache/htdocs/passwd</div><br />
becomes<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">mkdir -p 'a/new/dir<span style="color: red;">'\'' ; '\''</span>cat /etc/passwd > /usr/local/apache/htdocs/passwd'<br />
<br />
# while [ "`ls`" ]; do ls -F && cd "`ls`"; done<br />
a/<br />
new/<br />
dir<span style="color: red;">' ; '</span>cat /<br />
etc/<br />
...</div><br />
That didn't give us an exploit. So let's try escaping those intermediate quotes:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">a/new/dir<span style="color: red;">\' ; \'</span>cat /etc/passwd > /usr/local/apache/htdocs/passwd</div><br />
becomes<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">mkdir -p 'a/new/dir<span style="color: red;">\'\'' ; \'\''</span>cat /etc/passwd > /usr/local/apache/htdocs/passwd'</div><br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;"># while [ "`ls`" ]; do ls -F && cd "`ls`"; done<br />
a/<br />
new/<br />
dir<span style="color: red;">\' ; \'</span>cat /<br />
etc/<br />
...</div><br />
My simple function handles them both correctly with successive strong quote wrappings. It still isn't protected from other attacks like path traversal trickery using embedded .. but <b>shquote()</b> goes a long way in santizing input. I couldn't think of a way to break it. Please tell me if you can.Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-475658478418948032012-01-02T15:56:00.000-08:002012-01-20T23:22:45.170-08:0064-bit Stack PaddingI tracked down an interesting bug recently worth noting due to the lessons I learned about 64-bit computing. The bug was in some unit test code, essentially testing different types of a custom object. Here's a simplified example of the bug in a standalone program:<br />
<pre><a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line1"> 1</a> <b>#include <span style="color: green;"><stdio.h></span></b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line2"> 2</a> <b>#include <span style="color: green;"><stdlib.h></span></b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line3"> 3</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line4"> 4</a> <b>enum</b> <span style="color: #2040a0;">type</span> <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line5"> 5</a> <span style="color: #2040a0;">T0</span> = <span style="color: red;">0</span>,
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line6"> 6</a> <span style="color: #2040a0;">T1</span>,
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line7"> 7</a> <span style="color: #2040a0;">T2</span>,
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line8"> 8</a> <span style="color: #2040a0;">T3</span>,
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line9"> 9</a> <span style="color: #2040a0;">T4</span>,
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line10">10</a> <span style="color: #2040a0;">T5</span>,
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line11">11</a> <span style="color: #2040a0;">TMAX</span> = <span style="color: #2040a0;">T5</span>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line12">12</a> <b>}</b>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line13">13</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line14">14</a> <b>struct</b> <span style="color: #2040a0;">typed_item</span> <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line15">15</a> <b>enum</b> <span style="color: #2040a0;">type</span> <span style="color: #2040a0;">t</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line16">16</a> <b>void</b> *<span style="color: #2040a0;">data</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line17">17</a> <b>}</b>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line18">18</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line19">19</a> <b>void</b> <span style="color: #2040a0;">test1</span>(<b>void</b>)
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line20">20</a> <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line21">21</a> <b>struct</b> <span style="color: #2040a0;">typed_item</span> *<span style="color: #2040a0;">list</span>[<span style="color: #2040a0;">TMAX</span>];
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line22">22</a> <b>char</b> *<span style="color: #2040a0;">success_string</span> = <span style="color: green;">"Success"</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line23">23</a> <b>int</b> <span style="color: #2040a0;">i</span> = <span style="color: red;">0</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line24">24</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line25">25</a> <b>for</b> (<span style="color: #2040a0;">i</span> = <span style="color: #2040a0;">T0</span>; <span style="color: #2040a0;">i</span> <= <span style="color: #2040a0;">TMAX</span>; <span style="color: #2040a0;">i</span>++) <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line26">26</a> <span style="color: #2040a0;">list</span>[<span style="color: #2040a0;">i</span>] = <span style="color: #2040a0;">malloc</span>(<b>sizeof</b>(<b>struct</b> <span style="color: #2040a0;">typed_item</span>));
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line27">27</a> <span style="color: #2040a0;">list</span>[<span style="color: #2040a0;">i</span>]-><span style="color: #2040a0;">t</span> = <span style="color: #2040a0;">i</span>;
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line28">28</a> <b>}</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line29">29</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line30">30</a> <span style="color: #444444;">/* do tests on list */</span>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line31">31</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line32">32</a> <span style="color: #2040a0;">printf</span>(<span style="color: green;">"%s<span style="color: #77dd77;">\n</span>"</span>, <span style="color: #2040a0;">success_string</span>);
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line33">33</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line34">34</a> <span style="color: #444444;">/* cleanup */</span>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line35">35</a> <b>}</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line36">36</a>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line37">37</a> <b>int</b> <span style="color: #2040a0;">main</span>(<b>void</b>)
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line38">38</a> <b>{</b>
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line39">39</a> <span style="color: #2040a0;">test1</span>();
<a href="http://www.blogger.com/post-create.g?blogID=6761141672212693733" name="line40">40</a> <b>}</b>
</pre>Here's the console output:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">sdann32# gcc stack.c && ./a.out<br />
<br />
sdann32#</div><br />
I expected "Success" but instead get a non-printable character and a newline. Can you spot the bug?<br />
<br />
<code><span style="color: red;">- struct typed_item *list[TMAX];</span></code><br />
<code><span style="color: green;">+ struct typed_item *list[TMAX+1];</span></code><br />
<br />
This is a classic off-by-one error causing stack corruption. The for loop writes one entry past the end of the array and modifies the next variable on the stack. This was straightforward to track down in gdb. What was tricky, is that I couldn't repro such a simple bug consistently. When I ran this test on a different machine, everything worked fine.<br />
<br />
My company is in the process of converting from a 32-bit userspace, to a 64-bit userspace. Because of the way our feature branches are managed in a source tree, often I'm switching between development on a 32-bit build and a 64-bit build. This bug only reproed on a 32-bit platform as seen above. The 64-bit system succeeded:<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">sdann64# gcc stack.c && ./a.out<br />
Success<br />
sdann64# </div><br />
Since my initial belief was that this issue was caused by stack corruption, I dropped into gdb on the 64-bit machine to examine the <code>test1()</code> stack variables. To see changes in the stack variables easier, I've initialized the array with a known value of 2.<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">sdann64# gcc stack.c -g3 -O0<br />
sdann64# gdb ./a.out <br />
GNU gdb 6.1.1 [FreeBSD]<br />
Copyright 2004 Free Software Foundation, Inc.<br />
This GDB was configured as "amd64-marcel-freebsd"...<br />
(gdb) b test1<br />
Breakpoint 1 at 0x4005d9: file stack.c, line 21.<br />
(gdb) run<br />
Breakpoint 1, test1 () at stack.c:21<br />
21 struct typed_item *list[TMAX] = { (void*)2, (void*)2, (void*)2, (void*)2, (void*)2 };<br />
(gdb) n<br />
22 char *success_string = "Success";<br />
(gdb) n<br />
23 int i = 0;<br />
(gdb) n<br />
25 for (i = T0; i <= TMAX; i++) {<br />
(gdb) p $rbp<br />
$1 = (void *) 0x7fffffffe870<br />
(gdb) p $rsp<br />
$2 = (void *) 0x7fffffffe820<br />
(gdb) x /10xg $rsp<br />
0x7fffffffe820: 0x0000000800902040 0x0000000000000002<br />
0x7fffffffe830: 0x0000000000000002 0x0000000000000002<br />
0x7fffffffe840: 0x0000000000000002 0x0000000000000000<br />
0x7fffffffe850: 0x000000000040065d 0x00000000006fb08c<br />
0x7fffffffe860: 0x0000000000000001 0x0000000000000001</div><br />
Once past the local variable initialization instructions I check the frame base pointer ($rbp) and the frame stack pointer ($rsp) and see that there's a 0x50 difference between them, or 80 bytes. Listing this portion of the stack gives the local variables for the function.<br />
<br />
<div style="background-color: black; color: white; font-family: "Courier New",Courier,monospace;">(gdb) x /10xg $rsp<br />
0x7fffffffe820: <span style="color: cyan;">0x0000000000000002</span> <span style="color: cyan;">0x0000000000000002</span><br />
0x7fffffffe830: <span style="color: cyan;">0x0000000000000002 0x0000000000000002</span><br />
0x7fffffffe840: <span style="color: cyan;">0x0000000000000002</span> 0x0000000000000000<br />
0x7fffffffe850: <span style="color: magenta;">0x000000000040065d</span> <span style="color: yellow;">0x00000000</span><span style="color: red;">006fb08c</span><br />
0x7fffffffe860: <span style="color: red;">0x0000000000000001 0x0000000000000001</span></div><br />
Annotated with color above, the <span style="color: red;">red section</span> are saved registers and temporary variables. The three local variables are highlighted as such:<br />
<span style="background-color: cyan;"><br />
<code>struct typed_item *list[TMAX];</code></span><span style="background-color: magenta;"><br />
<code>char *success_string = "Success";</code></span><span style="background-color: yellow;"><br />
<code>int i = 0; </code></span><br />
<br />
Just as I suspected, when asking the compiler for 80 bytes of stack storage space, the allocation is padded to 96 bytes. It is this extra 16 bytes which allows my accidental off-by-one overwrite to have no functional affect on the program. I've never run into this issue on 32-bit platforms, so I'm now mindful that 64-bit stack padding can mask bugs that appear on other architectures. <br />
<br />
<span style="color: #cccccc;"><span style="color: black;">(All testing done with Intel x64 processor, FreeBSD 7.3, and gcc 4.2.)</span></span>Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0tag:blogger.com,1999:blog-6761141672212693733.post-73944463017950587272011-12-20T21:20:00.000-08:002011-12-30T23:53:56.170-08:00SDC 2011 Presentations OnlineThe SNIA Storage Developer Conference is held every September in sunny Santa Clara, CA and is attended by almost every file protocol developer in the world. All the geeks that do my job (SMB file server development) at all the major OS manufacturers (Microsoft, Apple, NetApp) get together to talk about odd bugs, watch presentations, and actually test interoperability in SMB lab.<br />
<br />
The benefit of this event is 3-fold:<br />
<ol><li>Networking with other professionals</li>
<li>Testing client/server interaction with the developers who wrote the stack, and can fix the bugs immediately.</li>
<li>Thoughtful, informative, presentations of past and future technology.</li>
</ol>While you have to attend to get the first two, access to the presentations has just been made available online by SNIA. In addition to the slide decks, several key presentations were recorded as well. All PDF and streaming videos are available at:<br />
<br />
<a href="http://www.snia.org/events/storage-developer2011/2011presentations">http://www.snia.org/events/storage-developer2011/2011presentations</a><br />
<br />
On the SMB front, I personally recommend the talk <b><span class="title">SMB 2.2: Bigger. Faster. Scalier</span></b><span class="title"><b>.</b> by </span><span class="author">David Kruse and Matthew George of Microsoft. Going into the details is a whole separate post, but I will say Microsoft has decided to be engineers again and implement some truly innovative features in their file protocol.</span>Steven Dannemanhttp://www.blogger.com/profile/06026772448188642610noreply@blogger.com0