Skip to content

Latest commit

 

History

History
620 lines (474 loc) · 18.2 KB

javascript.md

File metadata and controls

620 lines (474 loc) · 18.2 KB

+

Vanilla

Debugging

node inspect app.js
# Input:
# repl
debugger;

let user = null;
console.log({user});
console.log(arguments[0]);
console.table(["apples", "oranges", "bananas"]);

console.trace();
console.log(new Error().stack);

// Convert argument to string to avoid delayed evaluation in browser's dev tools including updates after log statement
let l = [];
l.push("1")
console.log(""+l); // Outputs: 1
l.push("2");

eval('console.log((function() { return !this; })());')
eval('"use strict"; console.log((function() { return !this; })());')

Object.defineProperty(navigator, 'webdriver', {
  get: () => {debugger; return false},
});

https://developer.mozilla.org/en-US/docs/Web/API/Console/count

env \
    DEBUG='*' \
    NODE_DEBUG='*' \
    node -e 'console.log(2+2)'

# syntax check
node --check ./index.js

node --stack-trace-limit=100 ./index.js

node --cpu-prof --heap-prof -e "require('request')"
# On browser: F12 > Memory > Load profile...
# On browser: F12 > Performance > Load profile...
# -- https://electronjs.org/docs/tutorial/performance

NODE_OPTIONS="--perf-basic-prof --perf-prof-unwinding-info" npm run
artillery quick -r 200 -d 0 http://localhost:3000
sudo perf record -F 99 -g -p $NODEJS_APP_PID -- sleep 60
sudo perf script -f --header > stacks.test.$(date --iso-8601)
# -- https://medium.com/yld-blog/cpu-and-i-o-performance-diagnostics-in-node-js-c85ea71738eb

# --trace-sync-io
#     Print a stack trace whenever synchronous I/O is detected after the first turn of the event loop.
# --zero-fill-buffers
#     Automatically zero-fills all newly allocated Buffer and SlowBuffer instances.

Tracing

handler = {
   apply: function(target, thisArg, argumentsList) {
   }
}
window.open = new Proxy(window.open, handler);
Element.prototype.appendChild = new Proxy(Element.prototype.appendChild, handler);

DevTools

# debug and run script
node --inspect app.js
# debug and break before running script
node --inspect-brk app.js
# => chrome://inspect/#devices > Open dedicated Devtools for Node
# -- https://marmelab.com/blog/2018/04/03/how-to-track-and-fix-memory-leak-with-nodejs.html

# ||
npm install -g ndb
ndb node app.js

V8

https://eternalsakura13.com/2018/08/02/v8_debug/

Linting

# Output: package.json
npm init
# Output: .eslintrc.js
npm install eslint --save-dev
./node_modules/.bin/eslint --init
# ||
npm install typescript

https://eslint.org/docs/user-guide/configuring

AST traversal, transformation

Testing:

Parsers:

Case Studies:

Linking

Enable

cd some-dep
# Output: symlink at global modules dir, target is local `some-dep` dir
npm link

cd my-app
# Output: symlink at `my-app` modules dir, target is global `some-dep` dir
npm link some-dep

Disable

cd my-app
npm uninstall --no-save some-dep && npm install 

cd some-dep
npm uninstall

Enabling breakpoints on some-dep

On launch.json:

"runtimeArgs": [
  "--preserve-symlinks"
]

https://medium.com/dailyjs/how-to-use-npm-link-7375b6219557

Globals

"env": {
    "node": true,
    "commonjs": true,
    "browser": true,
    "es6": true
}
"globals": {
    "process": true
}

https://stackoverflow.com/questions/50894000/eslint-process-is-not-defined

Benchmarking

Testing

  • Remove / Nullify URL parameters
  • Concurrent requests / sessions
    • e.g. remove element in one window, view same element in another window
  • Misalignments when updating dynamic content

WebSocket

curl \
    --http1.1 \
    --include \
    --no-buffer \
    --header "Connection: Upgrade" \
    --header "Upgrade: websocket" \
    --header "Host: example.com:80" \
    --header "Origin: http://example.com:80" \
    --header "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
    --header "Sec-WebSocket-Version: 13" \
    http://example.com:80/

Virtual DOM, diff patch trees

Frameworks

Browser Automation / Headless Browser

  • selenium webdriver
  • pyppeteer
    • ~/code/guides/ctf/TFNS---writeups/2020-04-12-ByteBanditsCTF/notes-app/sources/mynotes/visit_link.py
  • https://htmlunit.sourceforge.io/

Selectors

document.querySelectorAll('iframe').forEach( item =>
    console.log(item.contentWindow.document.body.querySelectorAll('a'))
)

setTimeout(() => Array.prototype.filter.call(document.querySelectorAll('a'), e => {return /evening/.test(e.textContent);})[0].click(), 2000)

Arrays

/*
Conversion to Array turns holes into undefined elements:
> Array.from([,])
[ undefined ]
> [...[,]]
[ undefined ]
*/
0 in [...[,]]; // true

/*
`in` detects properties, Array elements are props and holes are missing props:
> Reflect.ownKeys(['a', 'b'])
[ '0', '1', 'length' ]
> Reflect.ownKeys([,])
[ 'length' ]
*/
0 in [,]; // false

Introspection

Function.prototype.toString

o=window; do Object.getOwnPropertyNames(o).forEach(name => {console.log(name, o[name]);}); while(o = Object.getPrototypeOf(o));

// Globals
Object.entries(window)

Overriding

Generated code

node --print-opt-code

Cross-Origin script errors

Packages - Deduplication, Version Pinning

npm ls
yarn why

npm shrinkwrap

Packages - Updating version

vim package.json
rm package-lock.json
npm install

execute

npx some-package
# ~=
npm install some-package
./node_modules/.bin/some-package

symbols

  • Hidden but readable immutable properties, all values are unique
Object.getOwnPropertySymbols()

Promises

// https://github.com/petkaantonov/bluebird
Promise.longStackTraces();

// || manual
Promise.resolve()
    .then(outer)
    .then(inner)
    .then(evenMoreInner())
    .catch(function (err) {
            console.log(err.message);
            console.log(err.stack);
        });

async

async foo() {}
// ==
const foo = () => new Promise(resolve => resolve())

await Promise.all([..., ...])
// ==
return { foo: await x, bar: await y }

Deobfuscation

// Running:
// - VM or sandbox - e.g. https://repl.it
// - "Pause script execution" > drop script in console > hit enter

var _original_unescape = window.unescape;
window.unescape = function() {
    var u = _original_unescape.apply(this, arguments); // this calls the _original_ function
    console.log('unescape ', arguments, u);
    return u;
}

var _original_fromCharCode = String.fromCharCode;
String.fromCharCode = function() {
    var u = _original_fromCharCode.apply(this, arguments);
    console.log('fromcharcode ', arguments, u);
    return u;
}

var _original_eval = window.eval;
window.eval = function() {
    var args = arguments;
    console.log('eval ', arguments, u);
    var u = _original_eval.apply(this, arguments);
    debugger;
    return u;
}

bypass dummy debugger calls

  • ~/code/snippets/js/anti-anti-debugger.js
  • Log firefox's EvalKernel()
    #include "mozilla/Logging.h"
    // ...
    static bool EvalKernel(JSContext* cx, HandleValue v, EvalType evalType,
            AbstractFramePtr caller, HandleObject env,
            jsbytecode* pc, MutableHandleValue vp) {
        // ...
        using mozilla::LogLevel;
        static mozilla::LazyLogModule sLogger("example_logger");
        MOZ_LOG(sLogger, LogLevel::Info, ("Eval: %s", JS_EncodeStringToASCII(cx, v.toString()).get()));
        // ...
    }
  • Given integrity checks and debugger calls evaluated in a separate context, then replace firefox's debugger keyword
    • usage
      <script>
      window.oldSlice = Array.prototype.slice;
      Array.prototype.slice = function() {
          overriden_debugger;
          window.oldSlice(arguments);
      }
      </script>
      <script src="./foo.js"></script>
    • e.g. JScrambler
    • https://nullpt.rs/evading-anti-debugging-techniques/
      --- a/js/src/frontend/ReservedWords.h
      +++ b/js/src/frontend/ReservedWords.h
      @@ -20,7 +20,7 @@
         MACRO(catch, catch_, TokenKind::Catch)                \
         MACRO(const, const_, TokenKind::Const)                \
         MACRO(continue, continue_, TokenKind::Continue)       \
      -  MACRO(debugger, debugger, TokenKind::Debugger)        \
      +  MACRO(ticket_debugger, debugger, TokenKind::Debugger) \
         MACRO(default, default_, TokenKind::Default)          \
         MACRO(delete, delete_, TokenKind::Delete)             \
         MACRO(do, do_, TokenKind::Do)                         \
      --- a/js/src/vm/CommonPropertyNames.h
      +++ b/js/src/vm/CommonPropertyNames.h
      @@ -107,7 +107,7 @@
         MACRO_(currencySign, currencySign, "currencySign")  \
         MACRO_(day, day, "day")                             \
         MACRO_(dayPeriod, dayPeriod, "dayPeriod")           \
      -  MACRO_(debugger, debugger, "debugger")              \
      +  MACRO_(ticket_debugger, debugger, "ticket_debugger")\
         MACRO_(decimal, decimal, "decimal")

Running untrusted code

webpack

DevTools > Settings > Preferences > Sources > Check: Enable JavaScript source maps DevTools > Sources > Page > webpack://

https://www.mikeglopez.com/source-mapping-webpack-for-chrome/

observers

IntersectionObserver - monitored object has DOM tree changes IntersectionObserver - monitored object enters or exits another element or viewport

shadow root

// Reference: https://github.com/uBlock-user/uBO-Scriptlets/blob/master/scriptlets.js
const queryShadowRootElement = (shadowRootElement, rootElement) => {
    if (!rootElement) {
        return queryShadowRootElement(shadowRootElement, document.documentElement);
    }
    const els = rootElement.querySelectorAll(shadowRootElement);
    for (const el of els) { if (el) {return el;} }
    const probes = rootElement.querySelectorAll('*');
    for (const probe of probes) {
         if (probe.shadowRoot) {
             const shadowElement = queryShadowRootElement(shadowRootElement, probe.shadowRoot);
         if (shadowElement) { return shadowElement; }
         }
    }
    return null;
};

csp w/ 3rd party scripts

<% response.setHeader("Content-Security-Policy", "style-src 'unsafe-inline' 'self' https://cdn.cookielaw.org; frame-ancestors 'self'; default-src 'unsafe-inline' 'self' data: https://cdn.cookielaw.org https://code.jquery.com https://geolocation.onetrust.com;"); %>

heap

node --max-old-space-size=4096

tasks

grunt --verbose --debug
grunt --gruntfile app/templates/Gruntfile.js --base .

loading resources

  • Network Throttling = Enabled
  • Online > speed = Fast 3G

=> app waits for network request to complete despite not needing resource

https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API

cpu profiling

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

https://github.com/node-inspector/v8-profiler

memory profiling

node --track-heap-objects

node --expose-gc
# On browser: F12 > gc()

node --v8-options | grep -i 'expose\|prof'

https://github.com/bretcope/node-gc-profiler https://www.dynatrace.com/news/blog/understanding-garbage-collection-and-hunting-memory-leaks-in-node-js/

Chrome extension source

extension_id=jifpbeccnghkjeaalbbjmodiffmgedin
curl -L -o "$extension_id.zip" "https://clients2.google.com/service/update2/crx?response=redirect&os=mac&arch=x86-64&nacl_arch=x86-64&prod=chromecrx&prodchannel=stable&prodversion=44.0.2403.130&x=id%3D$extension_id%26uc"
unzip -d "$extension_id-source" "$extension_id.zip"

case studies

demoscene