25 KiB
기타 JS 트릭 및 관련 정보
htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!
- 사이버 보안 회사에서 일하시나요? 회사를 HackTricks에서 광고하고 싶으신가요? 아니면 PEASS의 최신 버전에 액세스하거나 HackTricks를 PDF로 다운로드하고 싶으신가요? SUBSCRIPTION PLANS를 확인해보세요!
- The PEASS Family를 발견해보세요. 독점적인 NFT 컬렉션입니다.
- 공식 PEASS & HackTricks 스웨그를 얻으세요.
- 💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter에서 저를 팔로우하세요 🐦@carlospolopm.
- 해킹 트릭을 공유하려면 hacktricks repo 및 hacktricks-cloud repo에 PR을 제출하세요.
Javascript Fuzzing
유효한 JS 주석 문자
//This is a 1 line comment
/* This is a multiline comment*/
#!This is a 1 line comment, but "#!" must to be at the beggining of the line
-->This is a 1 line comment, but "-->" must to be at the beggining of the line
for (let j = 0; j < 128; j++) {
for (let k = 0; k < 128; k++) {
for (let l = 0; l < 128; l++) {
if (j == 34 || k ==34 || l ==34)
continue;
if (j == 0x0a || k ==0x0a || l ==0x0a)
continue;
if (j == 0x0d || k ==0x0d || l ==0x0d)
continue;
if (j == 0x3c || k ==0x3c || l ==0x3c)
continue;
if (
(j == 47 && k == 47)
||(k == 47 && l == 47)
)
continue;
try {
var cmd = String.fromCharCode(j) + String.fromCharCode(k) + String.fromCharCode(l) + 'a.orange.ctf"';
eval(cmd);
} catch(e) {
var err = e.toString().split('\n')[0].split(':')[0];
if (err === 'SyntaxError' || err === "ReferenceError")
continue
err = e.toString().split('\n')[0]
}
console.log(err,cmd);
}
}
}
//From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z
// From: Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 43). Kindle Edition.
log=[];
for(let i=0;i<=0xff;i++){
for(let j=0;j<=0xfff;j++){
try {
eval(`${String.fromCodePoint(i,j)}%$£234$`)
log.push([i,j])
}catch(e){}
}
}
console.log(log)//[35,33],[47,47]
유효한 JS 개행 문자
In JavaScript, there are several characters that can be used to represent a new line. These characters can be useful in certain situations, such as when trying to obfuscate code or bypass filters that block traditional line breaks.
Here are some valid JavaScript new line characters:
\n
: This is the most common and widely supported new line character in JavaScript. It represents a line break.\r
: This character is used to represent a carriage return, which is another type of line break.\u2028
: This is the Unicode character for line separator. It can be used as a new line character in JavaScript.\u2029
: This is the Unicode character for paragraph separator. It can also be used as a new line character.
When using these characters, it's important to keep in mind that different environments may interpret them differently. For example, some JavaScript engines may treat \r
as a line break, while others may ignore it. It's always a good idea to test your code in the specific environment where it will be executed to ensure that the new line characters are interpreted correctly.
By using these valid JS new line characters, you can add flexibility to your code and potentially bypass certain security measures that rely on blocking traditional line breaks. However, it's important to use these techniques responsibly and ethically, and only in the context of authorized penetration testing or security research.
//Javascript interpret as new line these chars:
String.fromCharCode(10) //0x0a
String.fromCharCode(13) //0x0d
String.fromCharCode(8232) //0xe2 0x80 0xa8
String.fromCharCode(8233) //0xe2 0x80 0xa8
for (let j = 0; j < 65536; j++) {
try {
var cmd = '"aaaaa";'+String.fromCharCode(j) + '-->a.orange.ctf"';
eval(cmd);
} catch(e) {
var err = e.toString().split('\n')[0].split(':')[0];
if (err === 'SyntaxError' || err === "ReferenceError")
continue;
err = e.toString().split('\n')[0]
}
console.log(`[${err}]`,j,cmd);
}
//From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z
함수 호출에서 유효한 JS 공백
In some cases, web application filters may block certain characters or strings that are commonly used in JavaScript code. However, there are alternative ways to bypass these filters and execute arbitrary JavaScript code. One such technique involves using valid JavaScript spaces in function calls.
In JavaScript, spaces are typically used to separate different elements of a function call, such as the function name and its arguments. However, JavaScript allows for different types of spaces, including regular spaces, non-breaking spaces, and zero-width spaces.
By using these alternative spaces, you can obfuscate your JavaScript code and bypass filters that only block regular spaces. Here are a few examples:
- Regular space:
alert('Hello World')
- Non-breaking space:
alert('Hello\u00A0World')
- Zero-width space:
alert('Hello\u200BWorld')
When these function calls are executed, they will produce the same result as the regular space version. However, web application filters that only block regular spaces may not detect these alternative spaces, allowing the execution of the JavaScript code.
It's important to note that while this technique can bypass certain filters, it may not work in all cases. Web application filters can be configured to block specific characters or strings, including alternative spaces. Therefore, it's crucial to thoroughly test the effectiveness of this technique in the specific context of the target application.
// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 40-41). Kindle Edition.
// Check chars that can be put in between in func name and the ()
function x(){}
log=[];
for(let i=0;i<=0x10ffff;i++){
try {
eval(`x${String.fromCodePoint(i)}()`)
log.push(i)
}catch(e){}
}
console.log(log)v//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,813 232,8233,8239,8287,12288,65279
문자열 생성에 사용할 수 있는 유효한 문자
The following characters can be used to generate strings:
다음 문자들은 문자열을 생성하는 데 사용될 수 있습니다:
-
Alphanumeric characters (A-Z, a-z, 0-9)
-
Special characters (!, @, #, $, %, ^, &, *, (, ), -, _, +, =, [, ], {, }, |, , :, ;, ", ', <, >, ,, ., ?, /)
-
Whitespace characters (space, tab, newline)
-
알파벳 문자 (A-Z, a-z, 0-9)
-
특수 문자 (!, @, #, $, %, ^, &, *, (, ), -, _, +, =, [, ], {, }, |, , :, ;, ", ', <, >, ,, ., ?, /)
-
공백 문자 (공백, 탭, 개행)
// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 41-42). Kindle Edition.
// Check which pairs of chars can make something be a valid string
log=[];
for(let i=0;i<=0x10ffff;i++){
try {
eval(`${String.fromCodePoint(i)}%$£234${String.fromCodePoint(i)}`)
log.push(i)
}catch(e){}
}
console.log(log) //34,39,47,96
//single quote, quotes, backticks & // (regex)
Surrogate Pairs BF (대리 쌍 BF)
이 기술은 XSS에는 그리 유용하지 않지만 WAF 보호를 우회하는 데 유용할 수 있습니다. 이 파이썬 코드는 2바이트를 입력으로 받아 첫 번째 바이트가 High 대리 쌍의 마지막 바이트와 동일하고 마지막 바이트가 Low 대리 쌍의 마지막 바이트와 동일한 대리 쌍을 찾습니다.
def unicode(findHex):
for i in range(0,0xFFFFF):
H = hex(int(((i - 0x10000) / 0x400) + 0xD800))
h = chr(int(H[-2:],16))
L = hex(int(((i - 0x10000) % 0x400 + 0xDC00)))
l = chr(int(L[-2:],16))
if(h == findHex[0]) and (l == findHex[1]):
print(H.replace("0x","\\u")+L.replace("0x","\\u"))
더 많은 정보:
- https://github.com/dreadlocked/ctf-writeups/blob/master/nn8ed/README.md
- https://mathiasbynens.be/notes/javascript-unicode https://mathiasbynens.be/notes/javascript-encoding
javascript{}:
프로토콜 퍼징
// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 34). Kindle Edition.
log=[];
let anchor = document.createElement('a');
for(let i=0;i<=0x10ffff;i++){
anchor.href = `javascript${String.fromCodePoint(i)}:`;
if(anchor.protocol === 'javascript:') {
log.push(i);
}
}
console.log(log)//9,10,13,58
// Note that you could BF also other possitions of the use of multiple chars
// Test one option
let anchor = document.createElement('a');
anchor.href = `javascript${String.fromCodePoint(58)}:alert(1337)`;
anchor.append('Click me')
document.body.append(anchor)
// Another way to test
<a href="javascript:alert(1337)">Test</a>
URL 퍼징
URL 퍼징은 웹 응용 프로그램에서 발생할 수 있는 취약점을 찾기 위한 기술입니다. 이 기술은 웹 애플리케이션의 URL에 다양한 입력 값을 주입하여 예상치 못한 동작을 유발하고 취약점을 찾는 것을 목표로 합니다. URL 퍼징은 주로 다음과 같은 과정을 거칩니다.
- URL의 각 파라미터에 대해 다양한 값을 주입합니다.
- 주입한 값이 웹 애플리케이션에서 어떻게 처리되는지 확인합니다.
- 예상치 못한 동작이나 취약점을 발견하면 해당 취약점을 악용하여 공격을 수행합니다.
URL 퍼징은 웹 애플리케이션에서 발생할 수 있는 다양한 취약점을 찾는 데 유용한 기술입니다. 이를 통해 XSS(Cross-Site Scripting), SQL Injection, 경로 조작 등의 취약점을 발견할 수 있습니다.
// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 36-37). Kindle Edition.
// Before the protocol
a=document.createElement('a');
log=[];
for(let i=0;i<=0x10ffff;i++){
a.href = `${String.fromCodePoint(i)}https://hacktricks.xyz`;
if(a.hostname === 'hacktricks.xyz'){
log.push(i);
}
}
console.log(log) //0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32
// Between the slashes
a=document.createElement('a');
log=[];
for(let i=0;i<=0x10ffff;i++){
a.href = `/${String.fromCodePoint(i)}/hacktricks.xyz`;
if(a.hostname === 'hacktricks.xyz'){
log.push(i);
}
}
console.log(log) //9,10,13,47,92
HTML Fuzzing
HTML Fuzzing은 웹 응용 프로그램에서 발생할 수 있는 취약점을 찾기 위해 자동화된 테스트 기법입니다. 이 기법은 입력 필드, 링크, 이미지 등과 같은 HTML 요소에 대해 임의의 데이터를 주입하여 애플리케이션의 응답을 분석합니다. 이를 통해 잠재적인 XSS (Cross-Site Scripting) 취약점을 찾을 수 있습니다.
HTML Fuzzing은 다양한 페이로드를 사용하여 입력 필드에 주입합니다. 이러한 페이로드는 스크립트 태그, 이스케이프된 문자열, HTML 엔티티 등을 포함할 수 있습니다. 이를 통해 애플리케이션의 취약한 부분을 식별하고, 공격자가 악성 스크립트를 삽입하여 사용자의 브라우저에서 실행되도록 할 수 있습니다.
HTML Fuzzing은 자동화된 도구를 사용하여 수행할 수 있습니다. 이러한 도구는 대량의 페이로드를 생성하고, 이를 자동으로 입력 필드에 주입하여 응용 프로그램의 응답을 분석합니다. 이를 통해 취약점을 식별하고, 개발자에게 보고서를 제공하여 보안 문제를 해결할 수 있도록 도와줍니다.
HTML Fuzzing은 웹 응용 프로그램의 보안을 강화하기 위해 중요한 기술입니다. 취약점을 식별하고 해결함으로써 악의적인 공격으로부터 사용자의 개인 정보와 시스템을 보호할 수 있습니다.
// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 38). Kindle Edition.
// Fuzzing chars that can close an HTML comment
let log=[];
let div = document.createElement('div');
for(let i=0;i<=0x10ffff;i++){
div.innerHTML=`<!----${String.fromCodePoint(i)}><span></span>-->`;
if(div.querySelector('span')){
log.push(i);
}
}
console.log(log)//33,45,62
속성 분석
Portswigger의 Hackability inspector 도구는 자바스크립트 객체의 속성을 분석하는 데 도움이 됩니다. 확인하세요: https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E
.map js 파일
- .map js 파일을 다운로드하는 트릭: https://medium.com/@bitthebyte/javascript-for-bug-bounty-hunters-part-2-f82164917e7
- 이 도구를 사용하여 이러한 파일을 분석할 수 있습니다. https://github.com/paazmaya/shuji
"--" 할당
감소 연산자 --
도 할당 연산자입니다. 이 연산자는 값을 가져와서 1만큼 감소시킵니다. 값이 숫자가 아닌 경우 NaN
으로 설정됩니다. 이를 사용하여 환경에서 변수의 내용을 제거할 수 있습니다.
함수 트릭
.call과 .apply
함수의 .call
메서드는 함수를 실행하는 데 사용됩니다.
기본적으로 첫 번째 인수로는 this
의 값을 기대하며, 아무것도 제공되지 않으면 **window
**가 그 값이 됩니다(단, **strict mode
**가 사용되지 않는 한).
function test_call(){
console.log(this.value); //baz
}
new_this={value:"hey!"}
test_call.call(new_this);
// To pass more arguments, just pass then inside .call()
function test_call() {
console.log(arguments[0]); //"arg1"
console.log(arguments[1]); //"arg2"
console.log(this); //[object Window]
}
test_call.call(null, "arg1", "arg2")
// If you use the "use strict" directive "this" will be null instead of window:
function test_call() {
"use strict";
console.log(this); //null
}
test_call.call(null)
//The apply function is pretty much exactly the same as the call function with one important difference, you can supply an array of arguments in the second argument:
function test_apply() {
console.log(arguments[0]); //"arg1"
console.log(arguments[1]); //"arg2"
console.log(this); //[object Window]
}
test_apply.apply(null, ["arg1", "arg2"])
화살표 함수
화살표 함수를 사용하면 한 줄로 함수를 더 쉽게 생성할 수 있습니다 (이해한다면).
// Traditional
function (a){ return a + 1; }
// Arrow forms
a => a + 100;
a => {a + 100};
// Traditional
function (a, b){ return a + b + 1; }
// Arrow
(a, b) => a + b + 100;
// Tradictional no args
let a = 4;
let b = 2;
function (){ return a + b + 1; }
// Arrow
let a = 4;
let b = 2;
() => a + b + 1;
그래서, 이전의 대부분의 함수들은 사실상 쓸모가 없습니다. 왜냐하면 우리는 이러한 함수들을 저장하고 호출할 곳이 없기 때문입니다. plusone
함수를 생성하는 예시를 살펴보겠습니다:
// Traductional
function plusone (a){ return a + 1; }
//Arrow
plusone = a => a + 100;
Bind 함수
bind 함수는 this
객체와 주어진 매개변수를 수정하여 함수의 복사본을 생성하는 데 사용됩니다.
//This will use the this object and print "Hello World"
var fn = function ( param1, param2 ) {
console.info( this, param1, param2 );
}
fn('Hello', 'World')
//This will still use the this object and print "Hello World"
var copyFn = fn.bind();
copyFn('Hello', 'World')
//This will use the "console" object as "this" object inside the function and print "fixingparam1 Hello"
var bindFn_change = fn.bind(console, "fixingparam1");
bindFn_change('Hello', 'World')
//This will still use the this object and print "fixingparam1 Hello"
var bindFn_thisnull = fn.bind(null, "fixingparam1");
bindFn_change('Hello', 'World')
//This will still use the this object and print "fixingparam1 Hello"
var bindFn_this = fn.bind(this, "fixingparam1");
bindFn_change('Hello', 'World')
{% hint style="info" %}
**bind
**를 사용하여 함수를 호출할 때 사용될 this
객체를 조작할 수 있습니다.
{% endhint %}
함수 코드 누출
함수의 객체에 접근할 수 있다면 해당 함수의 코드를 얻을 수 있습니다.
function afunc(){
return 1+1;
}
console.log(afunc.toString()); //This will print the code of the function
console.log(String(afunc)); //This will print the code of the function
console.log(this.afunc.toString()); //This will print the code of the function
console.log(global.afunc.toString()); //This will print the code of the function
이름이 없는 함수의 경우, 여전히 함수 코드를 내부에서 출력할 수 있습니다:
(function (){ return arguments.callee.toString(); })()
(function (){ return arguments[0]; })("arg0")
다른 함수에서 함수의 코드(주석 포함)를 추출하는 임의의 방법들:
- 함수 문자열 변환:
Function.prototype.toString()
메서드를 사용하여 함수를 문자열로 변환하고, 필요한 코드 부분을 추출합니다. - 정규 표현식:
toString()
메서드를 사용하여 함수를 문자열로 변환한 후, 정규 표현식을 사용하여 필요한 코드 부분을 추출합니다. - 디버거: 브라우저의 개발자 도구를 사용하여 디버거를 실행하고, 함수의 코드를 단계별로 확인하면서 추출합니다.
- AST 분석: Abstract Syntax Tree (AST) 분석 도구를 사용하여 함수의 코드를 추출합니다.
- 소스 코드 역공학: 디컴파일러를 사용하여 함수의 소스 코드를 역공학화하고, 필요한 코드 부분을 추출합니다.
이러한 방법들은 함수의 코드를 추출하는 데 도움이 될 수 있으며, 코드 검토, 디버깅, 보안 분석 등 다양한 목적으로 활용될 수 있습니다.
(function (){ return retFunc => String(arguments[0]) })(a=>{/* Hidden commment */})()
(function (){ return retFunc => Array(arguments[0].toString()) })(a=>{/* Hidden commment */})()
(function (){ return String(this)}).bind(()=>{ /* Hidden commment */ })()
(u=>(String(u)))(_=>{ /* Hidden commment */ })
(u=>_=>(String(u)))(_=>{ /* Hidden commment */ })()
샌드박스 탈출 - window 객체 복구
Window 객체는 alert 또는 eval과 같이 전역으로 정의된 함수에 접근할 수 있게 해줍니다.
{% code overflow="wrap" %}
// Some ways to access window
window.eval("alert(1)")
frames
globalThis
parent
self
top //If inside a frame, this is top most window
// Access window from document
document.defaultView.alert(1)
// Access document from a node object
node = document.createElement('div')
node.ownerDocument.defaultView.alert(1)
// There is a path property on each error event whose last element is the window
<img src onerror=event.path.pop().alert(1337)>
// In other browsers the method is
<img src onerror=event.composedPath().pop().alert(1337)>
// In case of svg, the "event" object is called "evt"
<svg><image href=1 onerror=evt.composedPath().pop().alert(1337)>
// Abusing Error.prepareStackTrace to get Window back
Error.prepareStackTrace=function(error, callSites){
2 callSites.shift().getThis().alert(1337);
3 };
4 new Error().stack
// From an HTML event
// Events from HTML are executed in this context
with(document) {
with(element) {
//executed event
}
}
// Because of that with(document) it's possible to access properties of document like:
<img src onerror=defaultView.alert(1337)>
<img src onerror=s=createElement('script');s.append('alert(1337)');appendChild(s)>
{% endcode %}
값에 대한 접근 중단점
// Stop when a property in sessionStorage or localStorage is set/get
// via getItem or setItem functions
sessionStorage.getItem = localStorage.getItem = function(prop) {
debugger;
return sessionStorage[prop];
}
localStorage.setItem = function(prop, val) {
debugger;
localStorage[prop] = val;
}
// Stop when anyone sets or gets the property "ppmap" in any object
// For example sessionStorage.ppmap
// "123".ppmap
// Useful to find where weird properties are being set or accessed
// or to find where prototype pollutions are occurring
function debugAccess(obj, prop, debugGet=true){
var origValue = obj[prop];
Object.defineProperty(obj, prop, {
get: function () {
if ( debugGet )
debugger;
return origValue;
},
set: function(val) {
debugger;
origValue = val;
}
});
};
debugAccess(Object.prototype, 'ppmap')
테스트 페이로드를 자동으로 브라우저에서 접근하기
In some cases, it may be necessary to automatically access test payloads in a browser to verify if they trigger any vulnerabilities. This can be done using various methods:
Method 1: Redirecting to a Local Web Server
- Set up a local web server on your machine.
- Create a web page that includes the test payload.
- Configure the target application to redirect to the local web server when the payload is triggered.
- Access the target application and observe if the payload is executed by the browser.
Method 2: Using Browser Extensions
- Install a browser extension that allows you to modify HTTP requests.
- Configure the extension to intercept requests to the target application.
- Modify the request to include the test payload.
- Send the modified request and observe if the payload is executed by the browser.
Method 3: Using Headless Browsers
- Use a headless browser, such as Puppeteer or Selenium, to automate browser interactions.
- Write a script that navigates to the target application and includes the test payload.
- Execute the script and observe if the payload is executed by the browser.
By automatically accessing test payloads in a browser, you can efficiently verify if they have any impact on the target application's security.
//Taken from https://github.com/svennergr/writeups/blob/master/inti/0621/README.md
const puppeteer = require("puppeteer");
const realPasswordLength = 3000;
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
//Loop to iterate through different values
for (let i = 0; i < 10000; i += 100) {
console.log(`Run number ${i}`);
const input = `${"0".repeat(i)}${realPasswordLength}`;
console.log(` https://challenge-0621.intigriti.io/passgen.php?passwordLength=${input}&allowNumbers=true&allowSymbols=true×tamp=1624556811000`);
//Go to the page
await page.goto(
`https://challenge-0621.intigriti.io/passgen.php?passwordLength=${input}&allowNumbers=true&allowSymbols=true×tamp=1624556811000`
);
//Call function "generate()" inside the page
await page.evaluate("generate()");
//Get node inner text from an HTML element
const passwordContent = await page.$$eval(
".alert .page-content",
(node) => node[0].innerText
);
//Transform the content and print it in console
const plainPassword = passwordContent.replace("Your password is: ", "");
if (plainPassword.length != realPasswordLength) {
console.log(i, plainPassword.length, plainPassword);
}
await sleep(1000);
}
await browser.close();
})();
htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!
- 사이버 보안 회사에서 일하시나요? 회사를 HackTricks에서 광고하고 싶으세요? 아니면 PEASS의 최신 버전에 액세스하거나 HackTricks를 PDF로 다운로드하고 싶으세요? SUBSCRIPTION PLANS를 확인해보세요!
- The PEASS Family를 발견해보세요. 독점적인 NFT 컬렉션입니다.
- 공식 PEASS & HackTricks 스웨그를 얻으세요.
- 💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter에서 저를 팔로우하세요 🐦@carlospolopm.
- 해킹 트릭을 공유하려면 PR을 hacktricks repo 및 hacktricks-cloud repo 에 제출하세요.