diff --git a/scripts/ci_spellcheck_format.py b/.github/scripts/ci_spellcheck_format.py similarity index 100% rename from scripts/ci_spellcheck_format.py rename to .github/scripts/ci_spellcheck_format.py diff --git a/scripts/get_release_info.py b/.github/scripts/get_release_info.py similarity index 67% rename from scripts/get_release_info.py rename to .github/scripts/get_release_info.py index 1c577edc..ce95420e 100644 --- a/scripts/get_release_info.py +++ b/.github/scripts/get_release_info.py @@ -9,20 +9,36 @@ - `BLOG_POST_SLUG_TITLE` : - `BLOG_POST_AUTHOR` : """ -import requests + +from dataclasses import dataclass +from typing import Optional +import httpx import bs4 import time import os ROOT: str = "https://blahcat.github.io" -URL: str = f"{ROOT}/feeds/all.atom.xml" +ATOM_FEED_URL: str = f"{ROOT}/feeds/all.atom.xml" + + +@dataclass +class SocialMedia: + twitter: Optional[str] + mastodon: Optional[str] + discord: Optional[str] + github: Optional[str] + -time.sleep(10) +AUTHORS = { + "hugsy": SocialMedia("@_hugsy_", "@hugsy@infosec.exchange", "@crazy.hugsy", "hugsy") +} -h = requests.get(URL) +time.sleep(2) + +h = httpx.get(ATOM_FEED_URL) assert h.status_code == 200 -soup = bs4.BeautifulSoup(h.text, "lxml") +soup = bs4.BeautifulSoup(h.text, "xml") node = soup.find("entry") assert node is not None @@ -34,33 +50,29 @@ def get(x: str): def strip_html(html: str): - s = bs4.BeautifulSoup(html, features="html.parser") + s = bs4.BeautifulSoup(html, features="xml") return s.get_text() +def env(x: str): + os.system(f"echo {x} >> $GITHUB_ENV") + + title = get("title").text authors = [x.text for x in get("author").find_all("name")] published = get("published").text -url = ROOT + get("link")["href"] -slug = get("link")["href"][18:-5] +url = str(get("link")["href"]) +slug = str(get("link")["href"].rsplit("/")[-1]) summary = strip_html(get("summary").text)[:-3] + " [...]" -author_twitters = [] -for author in authors: - if author == "hugsy": - author_twitters.append("@_hugsy_") -# TODO automate this - +author_twitters = [ + AUTHORS[n].twitter for n in authors if n in AUTHORS and AUTHORS[n].twitter +] twitter_body = ( f"""New blog post: '{title}' by {' and '.join(author_twitters)} - {url}""" ) twitter_body = twitter_body[:280] - -def env(x: str): - os.system(f"echo {x} >> $GITHUB_ENV") - - env(f"""BLOG_POST_TITLE="{title}" """) env(f"""BLOG_POST_PUBLISHED_DATE="{published}" """) env(f"""BLOG_POST_URL={url}""") diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt new file mode 100644 index 00000000..f3c25827 --- /dev/null +++ b/.github/scripts/requirements.txt @@ -0,0 +1,2 @@ +httpx[cli] +bs4 diff --git a/.github/spellcheck.yml b/.github/spellcheck.yml index b15078eb..9d3ae2a4 100644 --- a/.github/spellcheck.yml +++ b/.github/spellcheck.yml @@ -8,6 +8,20 @@ matrix: - .github/wordlist.txt encoding: utf-8 pipeline: + - pyspelling.filters.context: + context_visible_first: true + escapes: \\[\\`~] + delimiters: + # Ignore anything in {{ }} + - open: '(?s)(?P *\{{2})' + close: '^(?P=open)$' + - open: '(?P\}{2})' + close: '(?P=open)' + # Ignore frontmatter (+++ / +++) + - open: '(?s)^(?P *\+{3})$' + close: '^(?P=open)$' + - open: '(?P\+{3})$' + close: '(?P=open)' - pyspelling.filters.markdown: markdown_extensions: - pymdownx.superfences diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5f51e27..392a5638 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,10 +20,11 @@ jobs: steps: - uses: actions/checkout@v4 with: + submodules: true token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' architecture: 'x64' cache: 'pip' - name: Build and publish the site @@ -32,8 +33,6 @@ jobs: source ~/.bashrc git config --global user.name "hugsy" git config --global user.email "hugsy@users.noreply.github.com" - git clone https://github.com/hugsy/attila.git /tmp/themes/attila - pelican-themes --install /tmp/themes/attila - pelican content -o output -s pelicanconf.py + zola build -o output ghp-import output --no-jekyll --branch=gh-pages --message="Generated new content" git push --force origin gh-pages diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 8dbd3beb..bd31834c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,6 +20,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v4.1.1 + with: + submodules: true - name: Restore lychee cache uses: actions/cache@v4.0.2 @@ -33,9 +35,19 @@ jobs: env: GITHUB_TOKEN: ${{secrets.LYCHEE_TOKEN}} with: - args: --exclude='^http://rawpixels.net/.*$' --exclude='^http://rawpixels.net/.*$' --exclude='^https://twitter.com/.*$' --exclude='^https://ctftime.org/.*$' --cache --max-cache-age 1w --exclude-all-private --threads 10 --timeout 30 --retry-wait-time 60 --user-agent 'Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0' --no-progress 'content/**/*.md' + args: --exclude='^file://.*$' --exclude='^http://rawpixels.net/.*$' --exclude='^http://rawpixels.net/.*$' --exclude='^https://twitter.com/.*$' --exclude='^https://ctftime.org/.*$' --cache --max-cache-age 1w --exclude-all-private --threads 10 --timeout 30 --retry-wait-time 60 --user-agent 'Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0' --no-progress 'content/**/*.md' fail: true + - name: Check anchors (setup) + uses: taiki-e/install-action@v2 + with: + tool: zola@0.19.1 + + - name: Check anchors (setup) + run: | + zola check + + spellcheck: name: Spell Checker runs-on: ubuntu-latest @@ -46,7 +58,7 @@ jobs: uses: actions/checkout@v4.1.1 - name: Spellcheck - uses: rojopolis/spellcheck-github-actions@0.36.0 + uses: rojopolis/spellcheck-github-actions@0.38.0 with: task_name: Markdown config_path: .github/spellcheck.yml @@ -55,5 +67,5 @@ jobs: - if: '!cancelled()' run: | if [ -f spellcheck-output.txt ]; then - python scripts/ci_spellcheck_format.py spellcheck-output.txt >> ${GITHUB_STEP_SUMMARY} + python .github/scripts/ci_spellcheck_format.py spellcheck-output.txt >> ${GITHUB_STEP_SUMMARY} fi diff --git a/.github/workflows/notify.yml b/.github/workflows/notify.yml index 91632864..bdfb24b1 100644 --- a/.github/workflows/notify.yml +++ b/.github/workflows/notify.yml @@ -23,13 +23,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' architecture: 'x64' cache: 'pip' - shell: bash run: | - python -m pip install -r scripts/requirements.txt - python scripts/get_release_info.py + python -m pip install -r .github/scripts/requirements.txt + python .github/scripts/get_release_info.py - uses: nearform-actions/github-action-notify-twitter@v1.2.0 with: twitter-app-key: ${{ secrets.TWITTER_CONSUMER_API_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fcc0ade6..03b2ff1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,13 +22,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' architecture: 'x64' cache: 'pip' - shell: bash run: | python -m pip install -r scripts/requirements.txt - python scripts/get_release_info.py + python .github/scripts/get_release_info.py - uses: ncipollo/release-action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -45,13 +45,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' architecture: 'x64' cache: 'pip' - shell: bash run: | python -m pip install -r scripts/requirements.txt - python scripts/get_release_info.py + python .github/scripts/get_release_info.py - name: Create the new GitHub Discussion uses: abirismyname/create-discussion@v1.x env: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..05a48f5d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "themes/zola-clean-blog"] + path = themes/zola-clean-blog + url = https://github.com/dave-tucker/zola-clean-blog diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..8e97b4ba --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "themes/*/templates/*.html": "jinja-html" + }, +} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index b436ba70..00000000 --- a/README.md +++ /dev/null @@ -1,8 +0,0 @@ -

- - -

- - diff --git a/config.toml b/config.toml new file mode 100644 index 00000000..3bb83d92 --- /dev/null +++ b/config.toml @@ -0,0 +1,56 @@ +# https://www.getzola.org/documentation/getting-started/configuration/ +base_url = "https://blahcat.github.io" +title = "BlahCats Blog" +description = "Tales of a binary encoded life..." +theme = "zola-clean-blog" +generate_feeds = true +feed_filenames = ["atom.xml", "rss.xml"] +author = "hugsy" +compile_sass = true +build_search_index = true +minify_html = true + +taxonomies = [ + { name = "categories", rss = true, paginate_by = 10 }, + { name = "tags", rss = true, paginate_by = 10 }, + { name = "authors" }, +] + +[markdown] +highlight_code = true +highlight_theme = "base16-ocean-dark" # https://www.getzola.org/documentation/getting-started/configuration/#syntax-highlighting +render_emoji = true +bottom_footnotes = true +smart_punctuation = true +external_links_target_blank = true +external_links_no_follow = true +external_links_no_referrer = true + +[slugify] +paths = "on" +taxonomies = "on" +anchors = "on" +paths_keep_dates = true + +[link_checker] +internal_level = "error" +external_level = "warn" + +[extra] +clean_default_bg_cover = "/img/blog-cover.png" + +clean_blog_menu = [ + { url = "$BASE_URL", name = "Home" }, + { url = "$BASE_URL/series", name = "Series" }, + { url = "$BASE_URL/notes", name = "Notes" }, + { url = "$BASE_URL/about", name = "About" }, + { url = "$BASE_URL/qemu", name = "Qemu VMs" }, +] + +clean_blog_social = [ + { icon = "fas fa-rss", url = "$BASE_URL/atom.xml" }, + { icon = "fab fa-twitter", url = "https://twitter.com/ctf_blahcat" }, + { icon = "fab fa-github", url = "https://github.com/blahcat" }, + { icon = "fab fa-youtube", url = "https://www.youtube.com/channel/UCDrgY65mRZWVoMiB5-VMqfg" }, + { icon = "fab fa-discord", url = "https://discord.gg/hSbqxxBgRX" }, +] diff --git a/content/2013-06-20-I_feel_lucky.md b/content/2013-06-20-I_feel_lucky.md index 8bb50bdc..16f3fe30 100644 --- a/content/2013-06-20-I_feel_lucky.md +++ b/content/2013-06-20-I_feel_lucky.md @@ -1,10 +1,13 @@ -title: I feel lucky - or why I wrote a FreeBSD 1-day in one day -author: hugsy -category: research -tags: freebsd, 1day, lpe -date: 2013-06-20 00:00 +0000 -modified: 2013-06-20 00:00 +0000 ++++ +title = "I feel lucky - or why I wrote a FreeBSD 1-day in one day" +authors = ["hugsy"] +date = 2013-06-20T00:00:00Z +updated = 2013-06-20T00:00:00Z +[taxonomies] +categories = ["research"] +tags = ["freebsd", "1day", "lpe"] ++++ Sometimes life gives you eggs for free, you just need to spend some time making an omelet. That's exactly what happened to me on a recent engagement for a client: a typical PHP webapp full of holes left me with a nice stable shell access. @@ -40,7 +43,9 @@ Index: sys/vm/vm_map.c It kindda gave a good pointer of where to start: the usual rule for setuid dictates that a write access should immediately imply losing the elevated privilege. But this is where the bug was: by `mmap` a setuid binary (which any user can do), I can then choose to `ptrace` the process, and use `PT_WRITE` command to overwrite the `mmap`-ed memory, effectively overwriting the setuid binary! -
 Note: I was in a rush, so my exploit is partially destructive as I overwrite directly the setuid binary. If you choose to use it, please make a copy to be able to restore it.
+{% note() %} +I was in a rush, so my exploit is partially destructive as I overwrite directly the setuid binary. If you choose to use it, please make a copy to be able to restore it. +{% end %} My exploit was in 4 parts: @@ -86,7 +91,7 @@ My exploit was in 4 parts: Done! Simply execute the target binary to get a root shell. -```shell +```bash $ id uid=1001(user) gid=1001(user) groups=1001(user) $ gcc -Wall ./mmap.c && ./a.out diff --git a/content/2013-12-23-read_write_process_memory_on_linux.md b/content/2013-12-23-read_write_process_memory_on_linux.md index 1bbf8fef..fe9aba8f 100644 --- a/content/2013-12-23-read_write_process_memory_on_linux.md +++ b/content/2013-12-23-read_write_process_memory_on_linux.md @@ -1,13 +1,17 @@ -title: Using new syscalls for read/write arbitrary memory on Linux. -author: hugsy -tags: linux, kernel, seccomp -date: 2013-12-23 00:00 +0000 -modified: 2013-12-23 00:00 +0000 -category: research ++++ +title = "Using new syscalls for read/write arbitrary memory on Linux." +authors = ["hugsy"] +date = 2013-12-23T00:00:00Z +updated = 2013-12-23T00:00:00Z + +[taxonomies] +tags = ["linux", "kernel", "seccomp"] +categories = ["research"] ++++ Even though well known methods exist to bypass ptrace deactivation on a process when spawning (fake `ptrace()` preloading, breakpoint on `ptrace()`, etc... ), it is trickier when process is already protected. -Thankfully Linux 3.2+ was generous enough to provide read/write capabilities to another process with 2 new system calls: `sys_process_vm_readv` and `sys_process_vm_writev`. (see [the source code](https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl#L319)). For our Windows friend, those new syscalls are similar to `ReadProcessMemory()` and `WriteProcessMemory()`. +Thankfully Linux 3.2+ was generous enough to provide read/write capabilities to another process with 2 new system calls: `sys_process_vm_readv` and `sys_process_vm_writev`. (see [the source code](https://github.com/torvalds/linux/blob/975f3b6da18020f1c8a7667ccb08fa542928ec03/arch/x86/entry/syscalls/syscall_64.tbl#L321)). For our Windows friend, those new syscalls are similar to `ReadProcessMemory()` and `WriteProcessMemory()`. The manual says: > These system calls transfer data between the address space of the calling process ("the local process") and the process identified by pid ("the remote process"). The data moves directly between the address spaces of the two processes, without passing through kernel space. diff --git a/content/2016-03-07-bkpctf-2016-simple-calc-writeup.md b/content/2016-03-07-bkpctf-2016-simple-calc-writeup.md index 4e6be329..d2e7cb54 100644 --- a/content/2016-03-07-bkpctf-2016-simple-calc-writeup.md +++ b/content/2016-03-07-bkpctf-2016-simple-calc-writeup.md @@ -1,13 +1,17 @@ -title: BKPCTF 2016 - Simple Calc -date: 2016-03-07 22:51:04 +1100 -author: hugsy -tags: pwn, gef, ida, bkpctf-2016, x86 -category: ctf -modified: 2016-03-07 22:51:04 +1100 ++++ +title = "BKPCTF 2016 - Simple Calc" +date = 2016-03-07T22:51:04Z +updated = 2016-03-07T22:51:04Z +authors = ["hugsy"] + +[taxonomies] +tags = ["pwn", "gef", "ida", "bkpctf-2016", "x86"] +categories = ["ctf"] ++++ ### Info ### -```shell +```bash ~/cur/simple_calc $ file b28b103ea5f1171553554f0127696a18c6d2dcf7 b28b103ea5f1171553554f0127696a18c6d2dcf7: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=3ca876069b2b8dc3f412c6205592a1d7523ba9ea, not stripped ~/cur/simple_calc $ checksec.sh --file b28b103ea5f1171553554f0127696a18c6d2dcf7 @@ -19,7 +23,7 @@ Partial RELRO No canary found NX enabled No PIE No RPATH No RU `simple_calc` offered a binary that expects us to make some calculations. It will ask for a number of calculations (say *N*) to perform and will `malloc()` *N*x4 bytes in the heap. If we decompile with [IDA](https://www.hex-rays.com/products/ida/), it'll look something like this: -![vuln_in_ida](https://i.imgur.com/aFaqYf6.png) +{{ img(src="https://i.imgur.com/aFaqYf6.png" title="vuln_in_ida") }} Then a loop of *N* iterations will commence, each iteration offering to perform one of the possible arithmetic operations, @@ -28,7 +32,7 @@ which take in 2 DWORD operands, and apply the function. What is worth noticing i both operands and result are stored in the `.bss` (therefore at predictable addresses). -``` +```asm [...] .bss:00000000006C4A84 add_operator_2 dd ? ; DATA XREF: adds+40 .bss:00000000006C4A84 ; adds+69 ... @@ -47,7 +51,7 @@ addresses). By exiting, `simple_calc` performs a `memcpy()` of the malloc-ed buffer (whose length is controlled by us) into a stack buffer (of length 0x28 bytes) located at $rbp+40h. -![overflow](https://i.imgur.com/0wcLH24.png) +{{ img(src="https://i.imgur.com/0wcLH24.png" title="overflow") }} It is then easy to spot the trivial stack buffer overflow. @@ -88,7 +92,7 @@ def pwn(s): We execute and a SIGSEGV was well caught (as seen with [`gef`](https://github.com/hugsy/gef)) : -![gef](https://i.imgur.com/rn4XSOR.png) +{{ img(src="https://i.imgur.com/rn4XSOR.png" title="gef") }} However, the faulty instruction is in the `free()` following the `memcpy()` and yet not in the return from the main function. @@ -115,7 +119,7 @@ def pwn(s): We try again, and we hit the SIGSEGV in the RET. Perfect, time to bypass NX. -``` +```asm Program received signal SIGSEGV, Segmentation fault. [...] 0x40157c mov edi,eax @@ -135,7 +139,7 @@ writable address, and write '/bin//sh' (we arbitrarily chose 0x6c3110 in the `.bss`). Using [`ropgadget`](https://github.com/JonathanSalwan/ROPgadget) makes it easier than ever: -``` +```asm 0x401c87: # pop rsi ; ret 0x6c3110: # our writable address 0x44db34: # pop rax ; ret diff --git a/content/2016-03-08-bkpctf-2016-complex-calc.md b/content/2016-03-08-bkpctf-2016-complex-calc.md index 01f8a21d..88d8558c 100644 --- a/content/2016-03-08-bkpctf-2016-complex-calc.md +++ b/content/2016-03-08-bkpctf-2016-complex-calc.md @@ -1,9 +1,13 @@ -title: BKPCTF 2016 - Complex Calc -author: hugsy -category: ctf -tags: pwn, gef , ida , bkpctf-2016 , x86 , heap-overflow -date: 2016-03-08 00:00:00 -modified: 2016-03-08 00:00:00 ++++ +title = "BKPCTF 2016 - Complex Calc" +authors = ["hugsy"] +date = 2016-03-08T00:00:00Z +updated = 2016-03-08T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","gef","ida","bkpctf-2016","x86","heap-overflow"] ++++ The challenge is the sequel to `simple_calc`. If you haven't read our [write-up](/posts/2016/03/07/bkpctf-2016-simple-calc-writeup.html), now is the time 😊 @@ -31,9 +35,9 @@ control). Let's do some bindiffing! One of my new toys for quite a few months now is IDA Python plugin [diaphora](https://github.com/joxeankoret/diaphora) by Joxean Koret (aka - matalaz). By diffing then, the issue is immediately visible: +{{ twitter(user="matalaz") }}). By diffing then, the issue is immediately visible: -![image_alt](https://i.imgur.com/0tkaNNT.png) +{{ img(src="https://i.imgur.com/0tkaNNT.png" title="image_alt") }} The `free()` function was modified so we cannot benefit from the graceful exit of the function by simply passing a NULL pointer. Now, `free()` will always @@ -49,7 +53,7 @@ work. So I will assume you know as well. To stand on common ground, here is what a heap chunk looks like: -![image_alt](https://i.imgur.com/EVnKlBg.png) +{{ img(src="https://i.imgur.com/EVnKlBg.png" title="image_alt") }} When `free()` is called, some checks are made to know how the chunk must be deallocated: @@ -64,7 +68,7 @@ deallocated: This actually shows quite well in the flow graph: -![image_alt](https://i.imgur.com/omGULMz.png) +{{ img(src="https://i.imgur.com/omGULMz.png" title="image_alt") }} Since we control what is written in the heap (same method than `simple_calc`), we can control whether we want to deallocate using `unlink` or `munmap` (simply @@ -114,7 +118,7 @@ now which value should we use then for operator_1 and operator_2 ? Let's go back to `free()` flow graph: -![image_alt](https://i.imgur.com/7ZEy4nD.png) +{{ img(src="https://i.imgur.com/7ZEy4nD.png" title="image_alt") }} As we see, several conditions must be filled: diff --git a/content/2016-03-14-0ctf-2016-warmup-write-up.md b/content/2016-03-14-0ctf-2016-warmup-write-up.md index 083e2d52..9c65eb88 100644 --- a/content/2016-03-14-0ctf-2016-warmup-write-up.md +++ b/content/2016-03-14-0ctf-2016-warmup-write-up.md @@ -1,9 +1,13 @@ -date: 2016-03-14 00:00:00 -modified: 2016-03-14 00:00:00 -title: 0ctf 2016 - Warmup write-up -author: hugsy -tags: pwn,gef,ida,0ctf-2016,x86 -category: ctf ++++ +title = "0ctf 2016 - Warmup write-up" +authors = ["hugsy"] +date = 2016-03-14T00:00:00Z +updated = 2016-03-14T00:00:00Z + +[taxonomies] +tags = ["pwn", "gef", "ida", "0ctf-2016", "x86"] +categories = ["ctf"] ++++ I participated to [0ctf](https://ctftime.org/team/4419/) but only had time to play for the reversing challenge `trace` (write-up coming up soon) during the competition @@ -41,7 +45,7 @@ linked file). Stack canary and PIE are not on, but NX is. The binary is really small, does not do much either, so the vulnerability is quite easy to find and trigger. -![vuln](https://i.imgur.com/jpU2YsD.png) +{{ img(src="https://i.imgur.com/jpU2YsD.png" title="vuln") }} At 0x08048174, We have a `read(socket, buffer, 52)` where buffer can only contain 32 bytes, so we have a classic stack overflow. A part of the challenge @@ -55,7 +59,7 @@ is however due to the fact that our controlled part is quite limited In addition to not having a lot of gadgets (the source was written in pure assembly), no libc, etc. 0ctf organizers added that -``` +```txt notice: This service is protected by a sandbox, you can only read the flag at /home/warmup/flag ``` diff --git a/content/2016-03-22-bctf-16-ruin.md b/content/2016-03-22-bctf-16-ruin.md index 0a29d210..3c4de05d 100644 --- a/content/2016-03-22-bctf-16-ruin.md +++ b/content/2016-03-22-bctf-16-ruin.md @@ -1,9 +1,13 @@ -title: BCTF 2016 - Ruin -date: 2016-03-21 22:51:04 +1100 -modified: 2016-03-21 22:51:04 +1100 -category: ctf -author: hugsy -tags: pwn,gef,bctf-2016,arm,heap-overflow,format-string ++++ +title = " BCTF 2016 - Ruin" +authors = ["hugsy"] +date = 2016-03-21T22:51:04Z +updated = 2016-03-21T22:51:04Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","gef","bctf-2016","arm","heap-overflow","format-string"] ++++ This is an ARM 32b exploitation challenge part of the [BCTF](https://ctftime.org/ctf/94) competition, which I've enjoyed playing with the team TheGoonies. During the competition, @@ -45,7 +49,7 @@ The real `main()` function starts at 0x00008A88 and starts by allocating on the heap (`malloc()`) an 8-byte chunk, then jump to a function at 0x89CC (which I've called `get_key_security`) to authenticate and unlock the safe. -![get-key-security](https://i.imgur.com/WhZ5QLW.png) +{{ img(src="https://i.imgur.com/WhZ5QLW.png" title="get-key-security") }} The `strncmp()` call trivially shows the expected initial key, in this case `security`. Once the safe is unlocked, 4 different operations are possible: @@ -58,7 +62,7 @@ The `strncmp()` call trivially shows the expected initial key, in this case not allocated, then the function invokes `malloc(8)`. Then it performs an `fgets()` to store 24 bytes from stdin (us!) into this buffer. We immediately spot a heap overflow here. -![heap-ovf](https://i.imgur.com/sTpxqBT.png) +{{ img(src="https://i.imgur.com/sTpxqBT.png" title="heap-ovf") }} 1. Sign the secret with your name (function @0x000088B8, `sign_name()`): if the `name` chunk is not NULL (i.e. already allocated), then the function returns. Otherwise, it calls the `read_int()` function at 0x0875C which prompts the user for the @@ -149,7 +153,7 @@ So what size do we need for the *name* chunk? We know that the *key* chunk can write 16 bytes, so 4 DWORD. And also, the target address must be aligned to 2 DWORD (8 bytes - because it is an ARM 32 bits). -![got](https://i.imgur.com/DQjJxu6.png) +{{ img(src="https://i.imgur.com/DQjJxu6.png" title="got") }} @@ -159,7 +163,7 @@ But we have a problem, we don't know where the heap pages are located in the mem `fread()`, which unlike `fgets()` does not append a NULL byte at the end of the string. -![auth-func](https://i.imgur.com/Wml7uwI.png) +{{ img(src="https://i.imgur.com/Wml7uwI.png" title="auth-func") }} This allows to leak addresses some precious bytes from the heap, doing something like this: @@ -232,7 +236,7 @@ def leave(s): ``` Which produces the following result in `gef`: -![control-pc](https://i.imgur.com/UdmAg6N.png) +{{ img(src="https://i.imgur.com/UdmAg6N.png" title="control-pc") }} Bingo! We control the execution flow! Good! But now where do we go? @@ -248,7 +252,7 @@ control flow to `printf@plt`. This way, if you can control the parameter of this call, you can use a regular format string attack to read/write everywhere!! The `read_int()` (at 0x875c) offers a perfect exploitation case: -![read-int-ida](https://i.imgur.com/81p3djs.png) +{{ img(src="https://i.imgur.com/81p3djs.png" title="read-int-ida") }} `fgets` at 0x878c allows us to provide 32 bytes in the stack, which will be given to `atoi` as a parameter. So if we overwrite `atoi@got` with the address @@ -281,7 +285,7 @@ the argument to `printf()`. By leaking the memory, we find that an address to the libc can be found (at least) at the offset 21: -![libc-leak](https://i.imgur.com/Q5UpCbc.png) +{{ img(src="https://i.imgur.com/Q5UpCbc.png" title="libc-leak") }} On the C library I tested, the `system()` function was located at an offset of 0x37524 from the base. So now, we know the address of `system()`: @@ -313,7 +317,7 @@ enter the command we want to execute, in this case `/bin/sh` will do: ``` The exploit is complete, we can run it: -![image_alt](https://i.imgur.com/Ei1aeLb.png) +{{ img(src="https://i.imgur.com/Ei1aeLb.png" title="image_alt") }} And as always, go [here](https://gist.github.com/hugsy/187f7dd80cb5bff20842) for the full exploit. diff --git a/content/2016-03-28-volgactf-2016-web-of-science.md b/content/2016-03-28-volgactf-2016-web-of-science.md index 084e977e..d7ef18a6 100644 --- a/content/2016-03-28-volgactf-2016-web-of-science.md +++ b/content/2016-03-28-volgactf-2016-web-of-science.md @@ -1,9 +1,13 @@ -date: 2016-03-28 00:00:00 -modified: 2016-03-28 00:00:00 -title: VolgaCTF 2016 - Web of Science -tags: pwn,volgactf-2016,x86 -author: hugsy -category: ctf ++++ +title = "VolgaCTF 2016 - Web of Science" +authors = ["hugsy"] +date = 2016-03-28T00:00:00Z +updated = 2016-03-28T00:00:00Z + +["taxonomies"] +tags = ["pwn","volgactf-2016","x86"] +categories = ["ctf"] ++++ ### Info ### @@ -25,7 +29,7 @@ Full RelRO: No It's a simple dynamically linked binary for x86-64. We have a canary stack however the stack is executable (the '90s says hello!), which `gef` confirms instantly: -![exec-stack](https://i.imgur.com/LfT3dt1.png) +{{ img(src="https://i.imgur.com/LfT3dt1.png" title="exec-stack") }} ### Vulnerabilities ### @@ -34,7 +38,7 @@ confirms instantly: The binary is full of vulnerabilities, many of which were automatically detected by the `format-string-helper` command from [GDB-GEF](https://github.com/hugsy/gef.git) -![fmt-str-gef](https://i.imgur.com/cqYmZLi.png) +{{ img(src="https://i.imgur.com/cqYmZLi.png" title="fmt-str-gef") }} Without any static analysis, we immediately spot that many (if not all) `printf()` calls are vulnerable to format string vulnerabilities, where we @@ -53,7 +57,7 @@ When adding a paper (`add_paper()` function), a stack buffer of 1096 bytes is allocated on the stack. It is then possible to populate different fields of this stack allocated paper as shown here: -![add-paper-fill-info](https://i.imgur.com/dTZmTgS.png) +{{ img(src="https://i.imgur.com/dTZmTgS.png" title="add-paper-fill-info") }} When saving the stack buffer is then copied to the .bss segment: @@ -85,7 +89,7 @@ The `view_paper()` function (at 0x400D52) receives a pointer to a paper and displays its information using `printf()` - which makes us understand what triggered the `gef` plugin for format string. -![view-paper](https://i.imgur.com/f7hs6qZ.png) +{{ img(src="https://i.imgur.com/f7hs6qZ.png" title="view-paper") }} ### Exploitation ### @@ -184,7 +188,7 @@ All done mate ! Final word (or image): -![image_alt](https://i.imgur.com/PjfFC2f.jpg) +{{ img(src="https://i.imgur.com/PjfFC2f.jpg" title="image_alt") }} Full exploit is : [gef-exploit.py](https://gist.github.com/hugsy/deae32e1da40e7b8c754) diff --git a/content/2016-04-01-hitb-teaser-2016-bakery.md b/content/2016-04-01-hitb-teaser-2016-bakery.md index 7ea2ae48..484de61c 100644 --- a/content/2016-04-01-hitb-teaser-2016-bakery.md +++ b/content/2016-04-01-hitb-teaser-2016-bakery.md @@ -1,9 +1,13 @@ -date: 2016-04-01 00:00:00 -modified: 2016-04-01 00:00:00 -title: HITB 2016 - Bakery write-up -author: hugsy -tags: pwn,hitb -category: ctf ++++ +title = "HITB 2016 - Bakery write-up" +authors = ["hugsy"] +date = 2016-04-01T00:00:00Z +updated = 2016-04-01T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","hitb"] ++++ I participated to [HITB Teaser CTF](https://ctftime.org/event/325/) only to have a bit of fun with there pwnable challenge(s) which I find usually fun and @@ -34,7 +38,7 @@ It is a baking program, that allows to build your own recipe. After printing the available ingredients, the `main` function does this (at 0x0400CBC) -![image_alt](https://i.imgur.com/yrFucNx.png) +{{ img(src="https://i.imgur.com/yrFucNx.png" title="image_alt") }} Which translates to the pseudo-code: ```c diff --git a/content/2016-05-09-asis-ctf-2016-feap.md b/content/2016-05-09-asis-ctf-2016-feap.md index 061eb237..fc40eb6c 100644 --- a/content/2016-05-09-asis-ctf-2016-feap.md +++ b/content/2016-05-09-asis-ctf-2016-feap.md @@ -1,9 +1,13 @@ -date: 2016-05-09 00:00:00 -modified: 2016-05-09 00:00:00 -title: ASIS CTF 2016 - feap write-up -author: hugsy -tags: pwn,asis-2016,heap-overflow -category: ctf ++++ +title = "ASIS CTF 2016 - feap write-up" +authors = ["hugsy"] +date = 2016-05-09T00:00:00Z +updated = 2016-05-09T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","asis-2016","heap-overflow"] ++++ ### Info ### @@ -36,7 +40,7 @@ hold 20 notes. A note has the following structure: For each note at offset *i*, the **total** size of the malloc-block can be found at the offset *i* of the table `notes_sizes` located at 0x6020b0, i.e. `notes_sizes[i] = sizeof(notes[i])`. -![image_alt](https://i.imgur.com/AhFMZH6.png) +{{ img(src="https://i.imgur.com/AhFMZH6.png" title="image_alt") }} To manipulate the notes, the program offers several options via a simple menu with @@ -107,7 +111,7 @@ content of a note, by editing its name of its body. When editing the body, the function calls `fgets()` on `note[i]->body`, but with a size to read of the entire chunk (name + body). -![edit_note_overflow](https://i.imgur.com/3ywKzd7.png) +{{ img(src="https://i.imgur.com/3ywKzd7.png" title="edit_note_overflow") }} So we have 64 bytes (i.e. sizeof(note.name) ) that we can overwrite in the next chunk. @@ -166,4 +170,4 @@ will want to delete. Put it all together in the [complete exploit](https://gist.github.com/hugsy/de228ac01bae2125481cae00790a3a88) and you get the flag: -![flag](https://i.imgur.com/t2wYPsl.png) +{{ img(src="https://i.imgur.com/t2wYPsl.png" title="flag") }} diff --git a/content/2016-05-23-defcon-ctf-2016-feedme.md b/content/2016-05-23-defcon-ctf-2016-feedme.md index 7c15381f..2eeb877b 100644 --- a/content/2016-05-23-defcon-ctf-2016-feedme.md +++ b/content/2016-05-23-defcon-ctf-2016-feedme.md @@ -1,9 +1,13 @@ -date: 2016-05-23 00:00:00 -modified: 2016-05-23 00:00:00 -title: DEFCON CTF 2016 - feedme -author: hugsy -tags: pwn,defcon-2016,x86,brop -category: ctf ++++ +title = "DEFCON CTF 2016 - feedme" +authors = ["hugsy"] +date = 2016-05-23T00:00:00Z +updated = 2016-05-23T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","defcon-2016","x86","brop"] ++++ ### Info ### @@ -73,7 +77,7 @@ Flags: [ carry parity adjust zero sign trap INTERRUPT direction overflow The interesting function is at 0x08049036: -![do-feedme](https://i.imgur.com/WLAWsAW.png) +{{ img(src="https://i.imgur.com/WLAWsAW.png" title="do-feedme") }} This function basically will: diff --git a/content/2016-05-24-defcon-ctf-2016-heapfun4u.md b/content/2016-05-24-defcon-ctf-2016-heapfun4u.md index 7075cacc..c5228c70 100644 --- a/content/2016-05-24-defcon-ctf-2016-heapfun4u.md +++ b/content/2016-05-24-defcon-ctf-2016-heapfun4u.md @@ -1,9 +1,13 @@ -date: 2016-05-24 00:00:00 -modified: 2016-05-24 00:00:00 -title: DEFCON CTF 2016 - heapfun4u -author: hugsy -tags: pwn,defcon-2016,x86,heap -category: ctf ++++ +title = "DEFCON CTF 2016 - heapfun4u" +authors = ["hugsy"] +date = 2016-05-24T00:00:00Z +updated = 2016-05-24T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","defcon-2016","x86","heap"] ++++ ### Info ### @@ -123,8 +127,7 @@ known thanks to the "Nice guy" command). So we need to: 1. free the 3rd chunk 1. free the 2nd chunk. -```bash - +```txt | size = N | | data | | .. | @@ -136,7 +139,6 @@ known thanks to the "Nice guy" command). So we need to: | data | | | | | - ``` Upon the 2nd free, we will gain control of the `head_free_list_ptr`: @@ -209,7 +211,7 @@ The stack layout is hard to fully control at the level of the `allocate_buffer()` function. However, this function is called by the `main()` function, which uses a very large buffer (0x100 bytes) to store values read from stdin: -```text +```txt .text:0000000000400905 ; int __cdecl main(int, char **, char **) .text:0000000000400905 main proc near .text:0000000000400905 @@ -219,7 +221,7 @@ function, which uses a very large buffer (0x100 bytes) to store values read from Additionally, its location is very easy to pinpoint: -```text +```txt | | RetAddr | context of | | SFP of main | main() | | size | @@ -313,8 +315,8 @@ id uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),116(sambashare) vagrant@ubuntu-wily-15:/home/vagrant$ ``` -At that time, my teammate from TheGoonies @rick2600 had also read the flag file, which was: +At that time, my teammate from TheGoonies {{ twitter(user="rick2600") }} had also read the flag file, which was: -```text +```txt The flag is: Oh noze you pwned my h33p. ``` diff --git a/content/2016-06-13-armpwn-challenge.md b/content/2016-06-13-armpwn-challenge.md index 046301d3..1d2bc77e 100644 --- a/content/2016-06-13-armpwn-challenge.md +++ b/content/2016-06-13-armpwn-challenge.md @@ -1,14 +1,17 @@ -title: ARMPWN challenge write-up -date: 2016-06-13 12:21:05 +1100 -modified: 2016-06-13 12:21:05 +1100 -comments: false -author: hugsy -tags: pwn,arm,gef,gdb,ida,ropgadget,pwntools -category: ctf ++++ +title = "ARMPWN challenge write-up" +authors = ["hugsy"] +date = 2016-06-13T12:21:05Z +updated = 2016-06-13T12:21:05Z -## Info ## +[taxonomies] +categories = ["ctf", "research"] +tags = ["pwn","arm","gef","gdb","ida","rop","pwntools"] ++++ -A few weeks ago, I came across a GitHub repository created by [@5aelo](https://twitter.com/5aelo){:target="_blank" class="fa fa-twitter"} called [armpwn](https://github.com/saelo/armpwn) for people wanting +# Info + +A few weeks ago, I came across a GitHub repository created by {{ twitter(user="5aelo") }} for people wanting to have a bit of ARM fun. I had recently spent some time adding new features and perfectionning old ones to my exploit helper for GDB, [`gef`](https://github.com/hugsy/gef.git) and I saw there a perfect practice case. On top of that, I had nothing better to do @@ -44,13 +47,13 @@ __Note__: since a solution to the challenge is available within the GitHub repo, I don't feel too bad publishing my own. -## Web Application attack ## +# Web Application attack Just like for a regular pentest, all we know here is that the port 80/tcp is open, and accessing to `/` redirect us to a page to turn on and off a LED (supposed connected to the GPIO on our RaspberryPi). Not exactly fancy... By sending a simple [`ncat`](https://nmap.org/ncat) request, things get suddenly more interesting: -![toadd](https://i.imgur.com/Zw0BH8c.png) +{{ img(src="https://i.imgur.com/Zw0BH8c.png" title="toadd") }} *__Hint__:* Other tools were tested and failed. The reason for that is that they parse and resolve the URL *before* sending it. So if I try to fuzz @@ -82,7 +85,7 @@ straight-forward, let's move on. Next, the binary analysis. -## Reversing the binary ## +# Reversing the binary We can use `IDA` to start with the static analysis. After a quick examination, the overall structure reveals itself quite clearly. @@ -113,14 +116,14 @@ look for the marker of end for HTTP headers (`CRLF`*2). If not found, it will keep iterating through the loop. Otherwise, the block read will search for the header `Content-Length` and if found, will call `strtol()` on it to convert the pointer into a long integer (let's call it `N`). -![image_alt](https://i.imgur.com/awC1RfU.png) +{{ img(src="https://i.imgur.com/awC1RfU.png" title="image_alt") }} This value will be used to call read `N` bytes from the socket and stored in local buffer of size 0xffc. The overflow comes clear as we controlled the size of the `Content-Length` header, we can forged an HTTP request whose body is big enough to corrupt the memory. -``` +```asm .text:000015DC MOV R1, #0 ; endptr .text:000015E0 MOV R2, #10 ; base .text:000015E4 BL strtol @@ -193,19 +196,19 @@ By using the script, we can visualize in `IDA` the execution flow, that confirms our PoC and highlights all the addresses in `$pc` executed. -![ida-graph-trace.png](https://i.imgur.com/NXc221Q.png) +{{ img(src="https://i.imgur.com/NXc221Q.png" title="ida-graph-trace.png") }} Using the `pattern` commands of `gef` we find out that we start overwriting the canary after sending 4042 bytes. -## Exploitation ## +# Exploitation ### Binary protections ### Even though we have a memory corruption, this is not enough since we have plenty of protection mechanism to defeat first ☹ -``` +```txt gef➤ checksec [+] checksec for '/home/pi/armpwn/bin/websrv' Canary: Yes @@ -256,7 +259,7 @@ In the pseudo-code earlier, we found that the `main` process was calling a function that we named `treat_requests()`. IDA shows us that this function is actually a loop to process one or more valid HTTP requests on the same socket. -![ida-screen-treat-requests](https://i.imgur.com/2DnSsUl.png) +{{ img(src="https://i.imgur.com/2DnSsUl.png" title="ida-screen-treat-requests") }} Which looks something like: ```c @@ -355,14 +358,14 @@ directly to the socket (which presumably holds the file description #4). Now run it, enjoy the shell and a good coffee ☕ -![armpwn](https://i.imgur.com/uzlxQx8.png) +{{ img(src="https://i.imgur.com/uzlxQx8.png" title="armpwn") }} The complete exploit code can be found [here](https://gist.github.com/hugsy/45d1c23f33f09126fe0838c1fe057687). -## Final notes ## +# Final notes -Thanks to @5aelo for this fun +Thanks to {{ twitter(user="5aelo") }} for this fun challenge. It is a good way to get acquainted with ARM exploitation, and is one of the reason why I build [`gef`](https://github.com/hugsy/gef.git) in a first diff --git a/content/2016-08-27-ruxmon-16-making-gdb-great-again.md b/content/2016-08-27-ruxmon-16-making-gdb-great-again.md index 0ca1e5e7..60a0f505 100644 --- a/content/2016-08-27-ruxmon-16-making-gdb-great-again.md +++ b/content/2016-08-27-ruxmon-16-making-gdb-great-again.md @@ -1,9 +1,13 @@ -title: Ruxmon 08/2016 - Making GDB great again -date: 2016-08-27 11:52:34 +1100 -modified: 2016-08-27 11:52:34 +1100 -author: hugsy -tags: ruxmon,gdb,python,gef -category: talk ++++ +title = "Ruxmon 08/2016 - Making GDB great again" +authors = ["hugsy"] +date = 2016-08-27T11:52:34Z +updated = 2016-08-27T11:52:34Z + +[taxonomies] +categories = ["talk"] +tags = ["ruxmon","gdb","python","gef"] ++++ ### Ruxmon August 2016: Making GDB great again ### @@ -13,6 +17,6 @@ I also gave demos of my tool [`gef`](https://github.com/hugsy/gef.git), an archi Find the [PDF slides here](http://christophe.alladoum.free.fr/public/ruxmon-08-16/ruxmon_2016-09_gdb_enhanced_features.pdf) -And send me your insults/feedbacks on [gef new IRC channel](https://webchat.freenode.net/?channels=##gef)! +And send me your insults/feedbacks on [gef new IRC channel](https://web.archive.org/web/20160303165817/https://webchat.freenode.net/?channels=##gef)! -![trump-meme](https://i.imgur.com/jlkM0P6.jpg) +{{ img(src="https://i.imgur.com/jlkM0P6.jpg" title="trump-meme") }} diff --git a/content/2016-09-06-twctf-2016-reverse-box-writeup.md b/content/2016-09-06-twctf-2016-reverse-box-writeup.md index a28e6249..df95539a 100644 --- a/content/2016-09-06-twctf-2016-reverse-box-writeup.md +++ b/content/2016-09-06-twctf-2016-reverse-box-writeup.md @@ -1,9 +1,13 @@ -date: 2016-09-06 00:00:00 -modified: 2016-09-06 00:00:00 -title: TWCTF 2016 - reverse_box writeup -author: hugsy -tags: twctf-2016,reverse,binary-ninja,gef,unicorn-engine -category: ctf ++++ +title = "TWCTF 2016 - reverse_box writeup" +authors = ["hugsy"] +date = 2016-09-06T00:00:00Z +updated = 2016-09-06T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["twctf-2016","reverse","binary-ninja","gef","unicorn-engine"] ++++ The `reverse_box` challenge of TWCTF 2016 was a warmup challenge (only 50 points), not really hard. There @@ -15,7 +19,7 @@ minutes. So I figured I could throw in my 50c and write this post. ### Info ### -The vulnerable file (sha1: 1e11da1636e4a6b71683de5c23634b98827d3b3d) was given with the description: +The vulnerable file (sha1: `1e11da1636e4a6b71683de5c23634b98827d3b3d`) was given with the description: ```bash $ ./reverse_box ${FLAG} @@ -34,7 +38,7 @@ $ file ./reverse_box Using [Binary-Ninja](https://binary.ninja) disassembler, we can see that the `main` function is not doing much, but something like -![binja-1](https://i.imgur.com/6B6g3Du.png) +{{ img(src="https://i.imgur.com/6B6g3Du.png" title="binja-1") }} ```c fill_buffer(buffer[0x100]); @@ -49,7 +53,7 @@ permutations and rotations on this buffer. This means that there is a finite number (exactly 256) of possible configurations for the buffer used to generate the hash. -![binja-2](https://i.imgur.com/oPlPALo.png) +{{ img(src="https://i.imgur.com/oPlPALo.png" title="binja-2") }} Since I was feeling lazy and didn't want to reverse the whole thing, I decided to use my tool [`gef`](https://github.com/hugsy/gef.git) and diff --git a/content/2017-01-24-armpwn-redux-canary-reloaded.md b/content/2017-01-24-armpwn-redux-canary-reloaded.md index 65d9f55a..151e058f 100644 --- a/content/2017-01-24-armpwn-redux-canary-reloaded.md +++ b/content/2017-01-24-armpwn-redux-canary-reloaded.md @@ -1,17 +1,23 @@ -date: 2017-01-24 00:00:00 -modified: 2017-01-24 00:00:00 -title: ARMPWN redux: canary reloaded -author: hugsy -tags: linux,pwn,arm,ssp,armpwn -header-img: "img/canary-header.png" -category: ctf ++++ +title = "ARMPWN redux: canary reloaded" +authors = ["hugsy"] +date = 2017-01-24T00:00:00Z +updated = 2017-01-24T00:00:00Z + +[taxonomies] +tags = ["linux","pwn","arm","ssp","armpwn"] +categories = ["ctf"] + +[extra] +header_img = "img/canary-header.png" ++++ > > __TL;DR__: It is possible to defeat stack canary protection when a binary is vulnerable to > arbitrary file read. > -## Intro ## +# Intro First of, Happy New Year 2017 ✌ @@ -27,8 +33,8 @@ the `procfs` structure. All the details regarding the following attack on the canary are explained in this blog post, so I will assume that you are familiar with it. If you're not: - * the full article is [here](https://www.elttam.com.au/blog/playing-with-canaries){:target="_blank"} - * the code repository is [there](https://github.com/elttam/canary-fun){:target="_blank"} + * the full article is [here](https://www.elttam.com.au/blog/playing-with-canaries) + * the code repository is [there](https://github.com/elttam/canary-fun) In the article, I imagined the attack scenario would apply perfectly well to a Web or FTP server, and would occur following those steps: @@ -36,7 +42,7 @@ Web or FTP server, and would occur following those steps: 1. dump `/proc/self/auxv` to get the `AT_RANDOM` location 2. read `/proc/self/mem` and force an `lseek` access to reach the location found above via - the [HTTP header Range](https://tools.ietf.org/html/rfc7233#page-8){:target="_blank"} (for + the [HTTP header Range](https://tools.ietf.org/html/rfc7233#page-8) (for instance `Range: bytes=<0xAT_RANDOM_LOCATION>-<0xAT_RANDOM_LOCATION+16>`) 3. Truncate the received buffer to `sizeof(register)` 4. Nullify the last byte (`result &= ~0xff`) @@ -44,21 +50,21 @@ Web or FTP server, and would occur following those steps: That was the theory, which made perfect sense, but I wanted a practice case. -Earlier this year, I [had some fun with ARMPWN](/posts/2016/06/13/armpwn-challenge-write-up.html){:target="_blank"}, a vulnerable web server -created by @5aelo to practice +Earlier this year, I [had some fun with ARMPWN](/posts/2016/06/13/armpwn-challenge-write-up.html), a vulnerable web server +created by {{ twitter(user="5aelo") }} to practice exploitation on ARM, so I have decided to use it for a practical, yet very realistic exploit case. You can download: - - [the new websrv.c here](https://gist.github.com/00d74ecac86297efc6772e415f307176){:target="_blank"} - - [or simply the patch here](https://gist.github.com/c2dbc3e3c11836dcebf53a2189f35976){:target="_blank"} + - [the new websrv.c here](https://gist.github.com/00d74ecac86297efc6772e415f307176) + - [or simply the patch here](https://gist.github.com/c2dbc3e3c11836dcebf53a2189f35976) ## Patch analysis This cheap patch provides to the "new" `websrv` the (pseudo-)capability to -[parse the HTTP Range header](https://gist.github.com/hugsy/00d74ecac86297efc6772e415f307176#file-websrv-c-L181-L201){:target="_blank"} +[parse the HTTP Range header](https://gist.github.com/hugsy/00d74ecac86297efc6772e415f307176#file-websrv-c-L181-L201) provided by the client. This is basically how modern Web servers (Apache, nginx) treat this header. @@ -95,7 +101,7 @@ Vector. This approach is a lot more stable and stealthier than canary brute-forcing, since we don't rely on any memory corruption/process crash to determine the valid bytes of the canary -[as we did before](/2016/06/12/armpwn-challenge#leaking-the-canary){:target="_blank"}. +[as we did before](/2016/06/12/armpwn-challenge#leaking-the-canary). ### Find AT_RANDOM from the Auxiliary Vector @@ -135,17 +141,17 @@ m+= "Range: bytes={:d}-{:d}\r\n\r\n".format(at_random_address,at_random_address+ s.send(m) ``` - -
 Warning: `yama/ptrace_scope` must be set to 0 to be able to read the process
+{% note(type="warning") %} +`yama/ptrace_scope` must be set to 0 to be able to read the process memory. - +{% end %} ### Fire! The final exploitation script which combines all the steps described above can be found [here](https://gist.github.com/hugsy/a462b398721bfb7e6bbd678b6d0e852b). -``` +```bash $ python armpwn_leak_canary.py [+] Connected to 'rpi2-1:80' [+] Leaking AUVX @@ -156,7 +162,7 @@ $ python armpwn_leak_canary.py To be we fetched the correct value for the canary of the remote process, we can use [this script](https://github.com/elttam/canary-fun/blob/master/read_canary_from_pid.py) locally to compare the values for the canary: -![image_alt](https://i.imgur.com/IWpuMIy.png) +{{ img(src="https://i.imgur.com/IWpuMIy.png" title="image_alt") }} diff --git a/content/2017-01-26-insomni-hack-ctf-2017-bender-safe.md b/content/2017-01-26-insomni-hack-ctf-2017-bender-safe.md index 97cc8ff9..819bff6f 100644 --- a/content/2017-01-26-insomni-hack-ctf-2017-bender-safe.md +++ b/content/2017-01-26-insomni-hack-ctf-2017-bender-safe.md @@ -1,9 +1,13 @@ -date: 2017-01-26 00:00:00 -modified: 2017-01-26 00:00:00 -title: Insomni'Hack CTF 2017: bender_safer -author: hugsy -category: ctf -tags: pwn,linux,insomnihack,mips,stack-overflow,rop,shellcode,keystone ++++ +title = "Insomni'Hack CTF 2017: bender_safer" +authors = ["hugsy"] +date = 2017-01-26T00:00:00Z +updated = 2017-01-26T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["pwn","linux","insomnihack","mips","stack-overflow","rop","shellcode","keystone"] ++++ [Insomni'Hack CTF 2017](https://web.archive.org/web/20170102081524/https://teaser.insomnihack.ch/) offered a series of 3 challenges (i.e. 3 different flags) on the same binary, called `bender_safe`: @@ -61,7 +65,7 @@ The binary execution starts where the challenge `bender_safe` left off, with the OTP validation. We then get into a simple menu offering 3 choices: -``` +```txt This is Bender's password vault storage I have 54043195528445952 bytes of memory for storage! Although 54043195528444928 of which is used to store my fembots videos...HiHiHi! @@ -94,7 +98,7 @@ store the number of passwords to store in 2 locations, a dedicated variable (i.e. `passwords[0]`, @ebp-0x410). The number of passwords is used as a counter for a loop that will read the passwords from stdin, thanks to the `read_passwords` function. -![image_alt](https://i.imgur.com/7UfE0bU.png) +{{ img(src="https://i.imgur.com/7UfE0bU.png" title="image_alt") }} After spending way too long spent trying to check for an arithmetic mistake, I reviewed more thoroughly the function `read_passwords`. @@ -103,7 +107,7 @@ The function `read_passwords` takes two arguments, a pointer to a buffer and a integer, which corresponds to the size of data to read. The buffer is populated one character at a time, in the following loop: -![image_alt](https://i.imgur.com/OYLowAm.png) +{{ img(src="https://i.imgur.com/OYLowAm.png" title="image_alt") }} The interesting bit starts around 0x401640: when a `\n` character is provided to fill the byte at offset `i` (i.e. `buffer[i]`), the function performs an additional @@ -155,7 +159,7 @@ gef➤ x/x 0x7fff62d8 And if we populate the 12 remaining passwords with "A"*102 the return address (`$ra` register) gets corrupted, which we can observe by taking the exit: -![image_alt](https://i.imgur.com/INggKTu.png) +{{ img(src="https://i.imgur.com/INggKTu.png" title="image_alt") }} ### Exploitation ### @@ -196,7 +200,7 @@ if __name__ == "__main__": And we now know that the PC is controlled at offset 921, as we are on a Big Endian architecture: -![image_alt](https://i.imgur.com/NYLt8XQ.png) +{{ img(src="https://i.imgur.com/NYLt8XQ.png" title="image_alt") }} #### ROP-ing to a fixed area #### @@ -311,7 +315,7 @@ r.send(sc) ``` > -> **Update**: as [@0xGrimmlin](https://twitter.com/0xGrimmlin) [mentioned](https://twitter.com/0xGrimmlin/status/824959540349112321), during the CTF, +> **Update**: as {{ twitter(user="0xGrimmlin](https://twitter.com/0xGrimmlin) [mentioned") }}, during the CTF, > the challenge was actually QEMU chroot-ed, so technically this shellcode would > not have worked, but you could similarly build another one doing > open/read/write(stdout) @@ -323,7 +327,7 @@ We have now all the components to launch our exploit. The final version is available [here](https://gist.github.com/hugsy/3e64b7cae4de38ba153a23e5491bff24). -![image_alt22](https://i.imgur.com/VJgWcia.png) +{{ img(src="https://i.imgur.com/VJgWcia.png" title="image_alt22") }} ### Conclusion ### diff --git a/content/2017-06-25-qemu-images-to-play-with.md b/content/2017-06-25-qemu-images-to-play-with.md index 7d6ac8b0..4e1e5655 100644 --- a/content/2017-06-25-qemu-images-to-play-with.md +++ b/content/2017-06-25-qemu-images-to-play-with.md @@ -1,25 +1,31 @@ -date: 2017-06-25 00:00:00 -modified: 2017-06-25 00:00:00 -title: Some Qemu images to play with -author: hugsy -tags: linux,pwn,debug,arm,mips,aarch64,powerpc,sparc -header-img: "img/qemu-img.png" -category: misc ++++ +title = "Some Qemu images to play with" +authors = ["hugsy"] +date = 2017-06-25T00:00:00Z +updated = 2017-06-25T00:00:00Z + +[taxonomies] +categories = ["misc"] +tags = ["linux","pwn","debug","arm","mips","aarch64","powerpc","sparc"] + +[extra] +header_img = "img/qemu-img.png" ++++ > **TL;DR** > Ready-to-play Qemu images for under-rated architectures (ARM, MIPS, PowerPC, > SPARC, AARCH64) to play with, with all the tools builtin to understand memory > corruption on non x86 environments -> [here](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA){:target="_blank"}. +> [here](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA). > **Update _(2018/05/15)_** > The Mega.NZ repository was cloned to Google Drive, available -> [here](https://drive.google.com/drive/folders/107uMlL_DS8yD2TS_0yrHXBDnLOj44a8P?usp=sharing){:target="_blank"}. +> [here](https://drive.google.com/drive/folders/107uMlL_DS8yD2TS_0yrHXBDnLOj44a8P?usp=sharing). ## Become a ninja on non-x86 architectures ! -A few weeks back, I came across [@Fox0x01](https://twitter.com/Fox0x01){:target="_blank" class="fa fa-twitter"} [tutorial](https://azeria-labs.com/writing-arm-assembly-part-1/){:target="_blank"} to get started with learning debugging and exploitation techniques on ARM. If you haven't checked it out, make sure you add this on your to-read list. +A few weeks back, I came across {{ twitter(user="Fox0x01") }} to get started with learning debugging and exploitation techniques on ARM. If you haven't checked it out, make sure you add this on your to-read list. I have been initially developing [`gef`](https://github.com/hugsy/gef.git) for the same reason, to learn more about non-x86 architectures. So in the same spirit of openness that Azeria has shown, I am releasing a few Qemu virtual machines to start immediately playing with ARM, MIPS, PowerPC and AARCH64 architectures! @@ -36,19 +42,19 @@ Those images are battery-included, development tools, compilation and debugging Without further ado: - * [Link to Mega.nz](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA){:target="_blank"} - * [Link to Google Drive](https://drive.google.com/drive/folders/107uMlL_DS8yD2TS_0yrHXBDnLOj44a8P?usp=sharing"){:target="_blank"} + * [Link to Mega.nz](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA) + * [Link to Google Drive](https://drive.google.com/drive/folders/107uMlL_DS8yD2TS_0yrHXBDnLOj44a8P?usp=sharing") Unless stated otherwise, `root` password is `root`, and an low privilege account called `user` is created. > **Update**: the current ARMv6 image is based on a Raspberry Pi image. Therefore, the username is `pi` , password `raspberry` and is sudoer NOPASSWD. I will update the image soon to fix this. -![vbox-qemu](/assets/images/vbox.png) +{{ img(src="/img/vbox.png" title="vbox-qemu") }} ## But why ? -Already existing fantastic projects such as [Vagrant](https://app.vagrantup.com/boxes/search){:target="_blank"} for Linux/*nix and [modern.ie](https://web.archive.org/web/20170306074002/https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/){:target="_blank"} for Windows help us getting quickly functional environments we can use in labs. But they are only providing Intel-based images. +Already existing fantastic projects such as [Vagrant](https://app.vagrantup.com/boxes/search) for Linux/*nix and [modern.ie](https://web.archive.org/web/20170306074002/https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/) for Windows help us getting quickly functional environments we can use in labs. But they are only providing Intel-based images. The closest thing to what I wanted when I started exploring exotic architectures was [aurel32 (now Debian Quick Image Baker) Qemu pages](https://people.debian.org/~gio/dqib/), which provides great Qemu images. Unfortunately, they are using extremely old kernels and/or Linux distributions, making it too hard for a quick plug-n-play experience. @@ -59,7 +65,7 @@ All the VMs come with 2 compiled ELF binaries: a very simple `hello-world` to st ## But I just wanna play with assembly... -So take a look at [this](https://github.com/hugsy/cemu){:target="_blank"}. +So take a look at [this](https://github.com/hugsy/cemu). ## Ok so what's next ? @@ -68,8 +74,8 @@ Well, those VMs were built from scratch using Qemu, which takes forever. I will Hope you'll enjoy it! -![buzz-qemu](https://i.imgflip.com/1ri3fi.jpg) +{{ img(src="https://i.imgflip.com/1ri3fi.jpg" title="buzz-qemu") }} -Oh and if you happen to be wandering in Black Hat Las Vegas 2017, come say hi at [the Black Hat Arsenal booth](https://www.blackhat.com/us-17/arsenal/schedule/index.html#gdb-enhanced-features-gef-8048){:target="_blank"} +Oh and if you happen to be wandering in Black Hat Las Vegas 2017, come say hi at [the Black Hat Arsenal booth](https://www.blackhat.com/us-17/arsenal/schedule/index.html#gdb-enhanced-features-gef-8048) Cheers! diff --git a/content/2017-07-14-building-a-debian-stretch-qemu-image-for-mipsel.md b/content/2017-07-14-building-a-debian-stretch-qemu-image-for-mipsel.md index 91c2ee99..d2221781 100644 --- a/content/2017-07-14-building-a-debian-stretch-qemu-image-for-mipsel.md +++ b/content/2017-07-14-building-a-debian-stretch-qemu-image-for-mipsel.md @@ -1,12 +1,18 @@ -date: 2017-07-14 00:00:00 -modified: 2017-07-14 00:00:00 -title: Building a Debian Stretch QEMU image for MIPSel -author: hugsy -tags: howto,qemu,mipsel,mips64el -cover: assets/images/qemu-img.png -category: tutorial ++++ +title = "Building a Debian Stretch QEMU image for MIPSel" +authors = ["hugsy"] +date = 2017-07-14T00:00:00Z +updated = 2017-07-14T00:00:00Z -## Building a Debian Stretch (9) QEMU image running MIPSel ## +[taxonomies] +tags = ["howto","qemu","mipsel","mips64el"] +categories = ["tutorial"] + +[extra] +header_img = "/img/qemu-img.png" ++++ + +# Building a Debian Stretch (9) QEMU image running MIPSel > **TL;DR** > Two new images, Debian Stretch on MIPSel and MIPS64el were added to @@ -18,11 +24,11 @@ category: tutorial After releasing [the QEMU images](/posts/2017/06/25/qemu-images-to-play-with.html) I've created to test [`GEF`](https://github.com/hugsy/gef), I've received tons of demands from people asking for more images, but also for some DYI procedures. -As [@Fox0x01](https://twitter.com/Fox0x01){:target="_blank" class="fa fa-twitter"} already covered fairly exhaustively [how to build an QEMU ARMv6 compatible VM][https://azeria-labs.com/emulate-raspberry-pi-with-qemu/){:target="_blank"}, through this blog post I intend to provide a step-by-step how-to on building a Debian Stretch Malta MIPS32el image. +As {{ twitter(user="Fox0x01") }}, through this blog post I intend to provide a step-by-step how-to on building a Debian Stretch Malta MIPS32el image. -
 Note:
+{% note() %} There is no miracle here, I've just spend a long time googling for solution every time I was facing a problem. This tuto is more for a being a personal reminder for the future times I need to build an image 😊 -
+{% end %} ### Pre-requisites ### @@ -74,16 +80,16 @@ kernel will try to decompress `initrd`. The reason is: Then your MIPSel (Malta-flavor) system boots, and you end up in the regular `ncurses` Debian installer. -![1.debian.installer.png"%](https://i.imgur.com/IqDge4n.png) +{{ img(src="https://i.imgur.com/IqDge4n.png" title="1.debian.installer.png"%") }} Let the installer do its magic. -![3.debian.partition.png](https://i.imgur.com/Lg6Db5x.png) +{{ img(src="https://i.imgur.com/Lg6Db5x.png" title="3.debian.partition.png") }} Since it's a VM for test and lab stuff, the guided partitioning is more than enough (and select `All files in one partition`). Feel free to tweak that part. -![2.debian.installation.png](https://i.imgur.com/iv31UxH.png) +{{ img(src="https://i.imgur.com/iv31UxH.png" title="2.debian.installation.png") }} I usually install only the minimum OS to get a running shell once I boot. For there I install everything from `apt-get`. With a proper `openssh-server` @@ -95,13 +101,13 @@ installed, I then create 2 scripts: Debian will detect no boot loader, and show the following warning: -![7.debian.end_installer.png](https://i.imgur.com/fuxZCDU.png) +{{ img(src="https://i.imgur.com/fuxZCDU.png" title="7.debian.end_installer.png") }} So remember to append `root=/dev/sda1` to `-append` option before running your Qemu. Then the installation will finish successfully: -![6.debian.complete.png](http://i.imgur.com/qFvh3cM.png) +{{ img(src="http://i.imgur.com/qFvh3cM.png" title="6.debian.complete.png") }} ### Fixing the last quirks ### @@ -146,7 +152,7 @@ $ qemu-system-mipsel -M malta -m 1G \ -nographic ``` -![9.first.boot.png](http://i.imgur.com/6h0Wxed.png) +{{ img(src="http://i.imgur.com/6h0Wxed.png" title="9.first.boot.png") }} On all the images I've created, Debian doesn't properly DHCP the Ethernet interface (get a wrong name for the interface), so it must be done manually at @@ -186,12 +192,12 @@ exit 0 ``` And the `ssh.sh`: + ```bash #!/bin/sh echo "Existing users : 'root/root' & 'user/user'" ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 22055 user@127.0.0.1 -- $* exit 0 - ``` For Windows, simply convert `script.sh` to Batch. @@ -199,12 +205,12 @@ For Windows, simply convert `script.sh` to Batch. ### Download the new images ### Since I've built in parallel a Malta MIPS32el and MIPS64el for this tutorial, -both have been added to the [folder on Mega.nz](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA){:target="_blank"} +both have been added to the [folder on Mega.nz](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA) The MIPS64el was created **exactly** the same way, except that QEMU required the proper CPU version to boot correctly: -``` +```bash $ qemu-system-mips64el -M malta -cpu MIPS64R2-generic -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.gz \ diff --git a/content/2017-08-01-gef-at-black-hat-arsenal-us-2017.md b/content/2017-08-01-gef-at-black-hat-arsenal-us-2017.md index 35b6a17b..9b9737ab 100644 --- a/content/2017-08-01-gef-at-black-hat-arsenal-us-2017.md +++ b/content/2017-08-01-gef-at-black-hat-arsenal-us-2017.md @@ -1,18 +1,24 @@ -date: 2017-08-01 00:00:00 -modified: 2017-08-01 00:00:00 -title: GEF at Black Hat Arsenal US 2017 -author: hugsy -tags: gef,blackhat,arsenal -cover: assets/images/gef-bh-usa-bg.png -category: talk ++++ +title = "GEF at Black Hat Arsenal US 2017" +authors = ["hugsy"] +date = 2017-08-01T00:00:00Z +updated = 2017-08-01T00:00:00Z -## GEF at Black Hat Arsenal US 2017 ## +[taxonomies] +categories = ["talk"] +tags = ["gef","blackhat","arsenal"] -I had the privilege to be invited to present my tool [`GEF`](https://github.com/hugsy/gef){:target="_blank"} at [Black Hat Arsenal](https://blackhat.com){:target="_blank"} organized by [ToolsWatch](https://toolswatch.org){:target="_blank"} in Las Vegas this year. +[extra] +header_img = "/img/gef-bh-usa-bg.png" ++++ + +# GEF at Black Hat Arsenal US 2017 + +I had the privilege to be invited to present my tool [`GEF`](https://github.com/hugsy/gef) at [Black Hat Arsenal](https://blackhat.com) organized by ToolsWatch in Las Vegas this year. I did prepare a bunch of things for this presentation, including a good polishing of the docs, and a series of screencasts for people new to the tool, so they can start using the best features straight away. -You can find the presentation slides [here](http://christophe.alladoum.free.fr/public/blackhat-2017/BH-USA-17-Alladoum-GDB-Enhanced-Features.pdf){:target="_blank"} and the [tutorial playlist on YouTube](https://goo.gl/1QAZM4){:target="_blank"} for the screencasts to start learning using it, and some of its best features. +You can find the presentation slides [here](http://christophe.alladoum.free.fr/public/blackhat-2017/BH-USA-17-Alladoum-GDB-Enhanced-Features.pdf) and the [tutorial playlist on YouTube](https://goo.gl/1QAZM4) for the screencasts to start learning using it, and some of its best features. Thank you to those who attended (especially with the collision with the lunch period ☹ ), and as usual if you have questions, feel free to reach out. diff --git a/content/2017-08-07-setting-up-a-windows-vm-lab-for-kernel-debugging.md b/content/2017-08-07-setting-up-a-windows-vm-lab-for-kernel-debugging.md index 03ede4e8..1d5a8204 100644 --- a/content/2017-08-07-setting-up-a-windows-vm-lab-for-kernel-debugging.md +++ b/content/2017-08-07-setting-up-a-windows-vm-lab-for-kernel-debugging.md @@ -1,10 +1,16 @@ -date: 2017-08-07 00:00:00 -modified: 2017-08-07 00:00:00 -title: Setting up a Windows VM lab for kernel debugging -tags: windows,kernel,debug,virtualbox -author: hugsy -cover: assets/images/win-kernel-debug/win8-setup-kernel-mode.png -category: tutorial ++++ +title = "Setting up a Windows VM lab for kernel debugging" +authors = ["hugsy"] +date = 2017-08-07T00:00:00Z +updated = 2017-08-07T00:00:00Z + +[taxonomies] +categories = ["tutorial"] +tags = ["windows","kernel","debug","virtualbox"] + +[extra] +header_img = "/img/win-kernel-debug/win8-setup-kernel-mode.png" ++++ This is the first on a series of posts on Windows kernel debugging and exploitation. @@ -56,7 +62,7 @@ VirtualBox to do the following: pipe/socket" is **unchecked**. -![image_alt](/assets/images/win-kernel-debug/dbg-uart-settings.png) +{{ img(src="/img/win-kernel-debug/dbg-uart-settings.png" title="image_alt") }} * in the "Network" tab, on top of the the default NAT-ed network created by @@ -64,14 +70,14 @@ VirtualBox to do the following: existing interface on the host (for example `vboxnet0`). -![image_alt](/assets/images/win-kernel-debug/dbg-network-settings.png) +{{ img(src="/img/win-kernel-debug/dbg-network-settings.png" title="image_alt") }} Now the debugger is ready, you need to install WinDBG as the kernel debugger. A quick way, is to use [`Chocolatey`](https://chocolatey.org/) in an administrator prompt to install it as such: -``` +```ps1 C:\> @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" C:\> choco install -y --force windbg @@ -95,29 +101,30 @@ To enable it, start the Windows 7 debuggee VM, open a `cmd.exe` as Administrator, and add another entry to the boot loader using `bcdedit` utility: -``` +```ps1 C:\> bcdedit /copy {current} /d "Windows 7 with kernel debug via COM" ``` Then enable debug mode on new entry UUID: -``` + +```ps1 C:\> bcdedit /debug {UUID-RETURNED-BY-FORMER-COMMAND} on ``` -![image_alt](/assets/images/win-kernel-debug/win7-bcdedit-enable-debug.png) +{{ img(src="/img/win-kernel-debug/win7-bcdedit-enable-debug.png" title="image_alt") }} Now instruct Windows serial communication as debugging medium, and use the "fastest" baud rate (i.e 115200 symbols/sec). Since we'll only use serial debugging for this VM, we can use the `bcdedit /dbgsettings` global switch. -``` +```ps1 C:\> bcdedit /dbgsettings serial debugport:1 baud rate:115200 ``` *Note*: if we wanted to set debug settings specific to one entry of the boot loader, we would've used `bcdedit /set` instead. For instance: -``` +```ps1 C:\> bcdedit /set {UUID-RETURNED-BY-FORMER-COMMAND} debugtype serial ``` @@ -127,7 +134,7 @@ Now, shutdown the VM and go to its settings on VirtualBox (**Machine** -> Pipe` as Port Mode. Last provide a path to file in the `Path/Address` field, for example `/tmp/win7-kd-pipe`. -![image_alt](/assets/images/win-kernel-debug/win7-vbox-settings.png) +{{ img(src="/img/win-kernel-debug/win7-vbox-settings.png" title="image_alt") }} The tickbox `Connect to existing pipe/socket` means that the debuggee will always have to be started **after** the debugger VM, or VirtualBox will throw an @@ -141,11 +148,11 @@ error. Start the debugger VM first and prepare WinDBG for kernel-mode debugging (Ctrl-K) by selecting COM as debug vector: -![image_alt](/assets/images/win-kernel-debug/win7-windbg-option.png) +{{ img(src="/img/win-kernel-debug/win7-windbg-option.png" title="image_alt") }} WinDBG will then wait for communications on COM1. -``` +```ps1 Microsoft (R) Windows Debugger Version 6.3.9600.17298 X86 Copyright (c) Microsoft Corporation. All rights reserved. @@ -162,12 +169,12 @@ Deferred srv*c:\syms*http://msdl.microsoft Start the debuggee, and when the boot loader menu shows up, select the entry named `Windows 7 with kernel debug via COM`. -![image_alt](/assets/images/win-kernel-debug/win7-boot-manager.png) +{{ img(src="/img/win-kernel-debug/win7-boot-manager.png" title="image_alt") }} As you see Windows already indicates that this entry will be in debug mode. And when you press Enter, the debugger VM will be attached to the debuggee. -![image_alt](/assets/images/win-kernel-debug/win7-debug-session.png) +{{ img(src="/img/win-kernel-debug/win7-debug-session.png" title="image_alt") }} You're now debugging the Windows 7 x86 VM kernel!! But as you'll see, Serial Port debugging will drastically slow down all operations on the debuggee. This @@ -199,7 +206,7 @@ System -> Advanced system settings -> on the Hardware tab). Expand "Network adapters" and select the 2nd device's properties menu. On the new window, the "Location" field will be required to assignate this interface for debugging: -![image_alt](/assets/images/win-kernel-debug/win8-network-controller-properties.png) +{{ img(src="/img/win-kernel-debug/win8-network-controller-properties.png" title="image_alt") }} This indicates us the bus parameters we will need to provide `bcdedit` later on, with the format `::` (in this case @@ -209,12 +216,12 @@ Now open an administrator prompt and use `bcdedit` utility to create a new entry to the boot manager like we did on Windows 7, and enable the debug mode for it. But unlike Windows 7, now we have to setup the network properties: -``` +```ps1 C:\> bcdedit /dbgsettings net hostip:ip.of.debugger.vm port:50000 key:Kernel.Debugging.Is.Fun C:\> bcdedit /set {dbgsettings} busparams .. ``` -![image_alt](/assets/images/win-kernel-debug/win8-setup-kernel-mode.png) +{{ img(src="/img/win-kernel-debug/win8-setup-kernel-mode.png" title="image_alt") }} ### Running the debugging session @@ -224,7 +231,7 @@ Start the debugger VM first and prepare WinDBG for kernel-mode debugging (Ctrl-K) by selecting NET as debug vector, and set the Port and Key adequately. WinDBG will then be waiting for new connection: -``` +```txt Microsoft (R) Windows Debugger Version 6.3.9600.17336 AMD64 Copyright (c) Microsoft Corporation. All rights reserved. @@ -238,13 +245,13 @@ Waiting to reconnect... Start the VM. When the boot loader menu shows up, select the one with the network kernel mode enabled -![image_alt](/assets/images/win-kernel-debug/win8-boot-loader.png) +{{ img(src="/img/win-kernel-debug/win8-boot-loader.png" title="image_alt") }} The debugger will show some activity immediately. Note that execution of the debuggee will not stop, so you may hit Ctrl-Break at any time to force an interruption: -![image_alt](/assets/images/win-kernel-debug/win8-success.png) +{{ img(src="/img/win-kernel-debug/win8-success.png" title="image_alt") }} In this post, we've presented 2 techniques for kernel debugging, depending on diff --git a/content/2017-08-14-a-primer-to-windows-x64-shellcoding.md b/content/2017-08-14-a-primer-to-windows-x64-shellcoding.md index 70345c2a..6356972b 100644 --- a/content/2017-08-14-a-primer-to-windows-x64-shellcoding.md +++ b/content/2017-08-14-a-primer-to-windows-x64-shellcoding.md @@ -1,10 +1,16 @@ -date: 2017-08-14 00:00:00 -modified: 2017-08-14 00:00:00 -title: A Primer to Windows x64 shellcoding -author: hugsy -cover: assets/images/win-kernel-debug/bg.png -tags: windows,kernel,debugging,pwn,token,shellcode -category: tutorial ++++ +title = "A Primer to Windows x64 shellcoding" +authors = ["hugsy"] +date = 2017-08-14T00:00:00Z +updated = 2017-08-14T00:00:00Z + +[taxonomies] +categories = ["tutorial"] +tags = ["windows","kernel","debugging","pwn","token","shellcode"] + +[extra] +header_img = "/img/win-kernel-debug/bg.png" ++++ Continuing on the path to Windows kernel exploitation... @@ -20,9 +26,9 @@ user-land? The classic technique is to steal the `System` process token and copy it into the structure of our targeted arbitrary (but unprivileged) process (say `cmd.exe`). -
 Note:
+{% note() %} Our target here will the Modern.IE Windows 8.1 x64 we created in the [previous post](/2017/08/07/setting-up-a-windows-vm-lab-for-kernel-debugging), that we'll interact with using `kd` via Network debugging. Refer to previous post if you need to set it up. -
+{% end %} # Stealing SYSTEM token using `kd` @@ -31,7 +37,7 @@ The [`!process`](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-process) extension of WinDBG provides a structured display of one or all the processes. -```text +```txt kd> !process 0 0 System PROCESS ffffe000baa6c040 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 @@ -43,7 +49,7 @@ This leaks the address of the `_EPROCESS` structure in the kernel, of the proces named `System`. Using `dt` will provide a lot more info (here, massively truncated to what interests us): -```text +```txt kd> dt _EPROCESS ffffe000baa6c040 ntdll!_EPROCESS +0x000 Pcb : _KPROCESS @@ -60,7 +66,7 @@ ntdll!_EPROCESS At `nt!_EPROCESS.Token` (+0x348) we get the process token, which holds a pointer to an ["Executive Fast Reference" structure](https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/sdk/include/ndk/extypes.h;h=feaf7b95df50f7a9d95108882a2cdd71263a675b;hb=HEAD#l418). -```text +```txt kd> dt nt!_EX_FAST_REF ffffe000baa6c040+348 +0x000 Object : 0xffffc000`2f405598 Void +0x000 RefCnt : 0y1000 @@ -70,7 +76,7 @@ kd> dt nt!_EX_FAST_REF ffffe000baa6c040+348 If we nullify the last nibble of the address (i.e. AND with -0xf on x64, -7 on x86), we end up having the `System` token's address: -```text +```txt kd> ? 0xffffc000`2f405598 & -f Evaluate expression: -70367951432304 = ffffc000`2f405590 @@ -87,15 +93,17 @@ kd> dt nt!_TOKEN ffffc000`2f405590 [...] ``` -
 Note: the WinDBG extension [`!token`](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-token) provides a more detailed (and parsed) output. You might to refer to it instead whenever you are analyzing tokens.
+{% note() %} +the WinDBG extension [`!token`](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-token) provides a more detailed (and parsed) output. You might to refer to it instead whenever you are analyzing tokens. +{% end %} So basically, if we create a process (say `cmd.exe`), and overwrite its token with the `System` token value we found (0xffffc0002f405590), our process will be running as `System`. Let's try! -![image_alt](/assets/images/win-kernel-debug/token-bump-via-windbg-1.png) +{{ img(src="/img/win-kernel-debug/token-bump-via-windbg-1.png" title="image_alt") }} We search our process using `kd`: -```text +```txt kd> !process 0 0 cmd.exe PROCESS ffffe000babfd900 SessionId: 1 Cid: 09fc Peb: 7ff6fa81c000 ParentCid: 0714 @@ -105,7 +113,7 @@ PROCESS ffffe000babfd900 Overwrite the offset 0x348 with the `SYSTEM` token pointer (0xffffc0002f405590). -```text +```txt kd> dq ffffe000bc043900+348 l1 ffffe000`bc043c48 ffffc000`30723426 kd> eq 0xffffe000babfd900+0x348 0xffffc0002f405590 @@ -113,7 +121,7 @@ kd> eq 0xffffe000babfd900+0x348 0xffffc0002f405590 And tada ... -![image_alt](/assets/images/win-kernel-debug/token-bump-via-windbg-2.png) +{{ img(src="/img/win-kernel-debug/token-bump-via-windbg-2.png" title="image_alt") }} Now we know how to transform any unprivileged process into a privileged one using `kd`. @@ -143,7 +151,7 @@ This is exactly the purpose of the routine `nt!PsGetCurrentProcess`, but since we can't call it directly (thank you ASLR), we can still check what is it doing under the hood: -```text +```txt kd> uf nt!PsGetCurrentProcess nt!PsGetCurrentProcess: fffff801`feb06e84 65488b042588010000 mov rax,qword ptr gs:[188h] @@ -158,7 +166,7 @@ kd> dps gs:188 l1 specifically the kernel thread (KTHREAD) `nt!KiInitialThread`). If we check the content of this structure at the offset 0xb8, we find the structure to the current process: -```text +```txt kd> dt nt!_EPROCESS poi(nt!KiInitialThread+b8) +0x000 Pcb : _KPROCESS [...] @@ -184,7 +192,7 @@ The processes are stored in the `ActiveProcessLinks` (offset 0x2e8) of the `nt!_EPROCESS` structure, via a `_LIST_ENTRY`, which is a doubly linked list in its simplest form: -```text +```txt kd> dt _LIST_ENTRY ntdll!_LIST_ENTRY +0x000 Flink : Ptr64 _LIST_ENTRY diff --git a/content/2017-08-18-first-exploit-in-windows-kernel-hevd.md b/content/2017-08-18-first-exploit-in-windows-kernel-hevd.md index 7a33fe3d..0d4ae678 100644 --- a/content/2017-08-18-first-exploit-in-windows-kernel-hevd.md +++ b/content/2017-08-18-first-exploit-in-windows-kernel-hevd.md @@ -1,10 +1,17 @@ -date: 2017-08-18 00:00:00 -modified: 2017-08-18 00:00:00 -title: First exploit in Windows Kernel (HEVD) -author: hugsy -cover: assets/images/win-kernel-debug/win8-setup-kernel-mode.png -tags: windows,kernel,debugging,pwn,stack-overflow -category: tutorial ++++ +title = "First exploit in Windows Kernel (HEVD)" +authors = ["hugsy"] + +date = 2017-08-18T00:00:00Z +updated = 2017-08-18T00:00:00Z + +[taxonomies] +categories = ["tutorial", "research"] +tags = ["windows","kernel","debugging","pwn","stack-overflow", "hevd"] + +[extra] +header_img = "/img/win-kernel-debug/win8-setup-kernel-mode.png" ++++ Hi there ✋ @@ -37,7 +44,7 @@ Download and unzip in the Windows 8.1 x64 debuggee VM: Then simply run the `OSR Driver Loader` and register the AMD64 version of `HEVD.sys`. -![image_alt](/assets/images/win-kernel-debug/register-hevd.png) +{{ img(src="/img/win-kernel-debug/register-hevd.png" title="image_alt") }} You can then start the service. @@ -60,7 +67,7 @@ located within one of the path defined in the `_NT_SYMBOL_PATH` environment variable. For example like this (you might need to adjust to your own configuration): -``` +```bat C:> set | find "_NT_SYMBOL_PATH" _NT_SYMBOL_PATH=SRV*C:\symbols*http://msdl.microsoft.com/download/symbols; C:> mkdir C:\symbols\hevd.pdb @@ -72,7 +79,7 @@ We can use WinDbg to check that: * the HEVD driver is properly loaded: -``` +```txt kd> lm m HEVD start end module name fffff800`c1e39000 fffff800`c1e42000 HEVD (deferred) @@ -80,7 +87,7 @@ fffff800`c1e39000 fffff800`c1e42000 HEVD (deferred) * WinDbg can retrieve its symbols: -``` +```txt kd> .sympath Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols @@ -96,7 +103,7 @@ fffff800`c1e3c000 HEVD!HotPatchBuffer = struct _PATCH_BUFFER Last, you may overwrite the `nt!Kd_Default_Mask` to increase/decrease Windows kernel verbosity from WinDbg -``` +```txt kd> ed nt!Kd_Default_Mask 0xf ``` @@ -105,7 +112,7 @@ print debug info. This can be done via the key `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter` in an Admin command prompt: -``` +```bat C:> reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter" /v DEFAULT /t REG_DWORD /d 0xf ``` @@ -142,7 +149,7 @@ NTSTATUS DriverEntry( WinDbg confirms that immediately: -``` +```txt kd> x HEVD!DriverEntry fffff800`c1e41008 HEVD!DriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING *) ``` @@ -156,7 +163,7 @@ as [`FILE_DEVICE_UNKNOWN`](https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/specifying-device-types) - or 0x22. -![DriverEntry in IDA](/assets/images/win-kernel-debug/hevd-stack-overflow-ida-driver-entry.png) +{{ img(src="/img/win-kernel-debug/hevd-stack-overflow-ida-driver-entry.png" title="DriverEntry in IDA") }} Then the `DriverObject` gets populated with the [structure members](https://msdn.microsoft.com/en-us/library/windows/hardware/ff544174(v=vs.85).aspx) including functions pointers, among which we find the IOCTL @@ -164,7 +171,7 @@ handler, `IrpDeviceIoCtlHandler`. This function dispatches [IOCTL](https://en.wikipedia.org/wiki/Ioctl) requests done from user-land to the HEVD driver. Every IOCTL is uniquely identified by a specific code, and the handler will basically do a big `switch(dwIoControlCode){...}` to execute the corresponding code. IDA is capable of pulling out for us: -![image_alt](/assets/images/win-kernel-debug/hevd-stack-overflow-ida-ioctl-1.png) +{{ img(src="/img/win-kernel-debug/hevd-stack-overflow-ida-ioctl-1.png" title="image_alt") }} In this first exploitation, we want to reach the `StackOverflowIoctlHandler`, and therefore need to send a @@ -184,7 +191,6 @@ like (in pseudo-C): HANDLE hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", ...); BOOL bResult = DeviceIoControl(hDevice, IOCTL_HEVD_STACK_OVERFLOW, lpBufferIn, dwBufferInLength, ...); - ``` @@ -233,7 +239,6 @@ RtlFillMemory(lpInBuffer, 0x1000, 'A'); // for now, let's just populate the stac BOOL bResult = DeviceIoControl(hDevice, IOCTL_HEVD_STACK_OVERFLOW, lpBufferIn, dwBufferInLength, ...); - ``` And move on to the dynamic analysis... @@ -250,10 +255,9 @@ be nice to have WinDbg break at the `ret` instruction of `TriggerStackOverflow`. Since ASLR is enabled, we can't just break at a fixed address and having to compute the address would be tedious, but fortunately, WinDbg, in its all awesomeness, -provides the command -_Break Unresolved_) which provides a clean way to circumvent this issue: +provides the command "Break Unresolved" (`bu`) which provides a clean way to circumvent this issue: -``` +```txt kd> uf HEVD!TriggerStackOverflow [...] 101 fffff801`8063e707 5f pop rdi @@ -267,12 +271,12 @@ kd> bu HEVD!TriggerStackOverflow+c8 We can now compile and run our first PoC, and wait for WinDbg to catch the breakpoint. -![image_alt](/assets/images/win-kernel-debug/hevd-stack-overflow-windbg-ret.png) +{{ img(src="/img/win-kernel-debug/hevd-stack-overflow-windbg-ret.png" title="image_alt") }} If we check the stack, we see that we've successfully overwritten the return address: -``` +```txt | | | ReturnAddr | | SFP | @@ -287,7 +291,7 @@ address: So we know that our user-land allocated must be something like: -``` +```txt 0x800 bytes 8 bytes 8 bytes _____________________^___________________ ___^____ ___^____ / \ / \ / \ @@ -339,7 +343,7 @@ version of Windows might produce an unexpected behavior. We can run it, and w00t ! -![image_alt](/assets/images/win-kernel-debug/hevd-stack-overflow-exploit.png) +{{ img(src="/img/win-kernel-debug/hevd-stack-overflow-exploit.png" title="image_alt") }} I've also added to the repository my WinDbg workspaces (for both user-mode and kernel-mode) along with a header file `hevd.h` with a few functions helping the diff --git a/content/2017-08-31-arbitrary-write-primitive-in-windows-kernel-hevd.md b/content/2017-08-31-arbitrary-write-primitive-in-windows-kernel-hevd.md index 9bd46f3b..4019ff16 100644 --- a/content/2017-08-31-arbitrary-write-primitive-in-windows-kernel-hevd.md +++ b/content/2017-08-31-arbitrary-write-primitive-in-windows-kernel-hevd.md @@ -1,10 +1,16 @@ -date: 2017-08-31 00:00:00 -modified: 2017-08-31 00:00:00 -title: Arbitrary Write primitive in Windows kernel (HEVD) -author: hugsy -category: tutorial -cover: assets/images/win-kernel-debug/hevd-www-hal-interrupt.png -tags: pwn,windows,hevd,kernel,pwn,write-what-where ++++ +date = 2017-08-31T00:00:00Z +updated = 2017-08-31T00:00:00Z +title = "Arbitrary Write primitive in Windows kernel (HEVD)" +authors = ["hugsy"] + +[taxonomies] +categories = ["tutorial", "research"] +tags = ["pwn","windows","hevd","kernel","pwn","write-what-where"] + +[extra] +header_img = "/img/win-kernel-debug/hevd-www-hal-interrupt.png" ++++ Back again to modern Windows kernel exploitation! @@ -25,7 +31,7 @@ After not that much effort in IDA by tracing down the IOCTL dispatching function callgraph, we spot the function `TriggerArbitraryOverwrite()` which can be reached via a IOCTL with a code of 0x22200B. The vulnerability is easy to spot: -![image_alt](/assets/images/win-kernel-debug/hevd-www-ida-vuln-spotting.png) +{{ img(src="/img/win-kernel-debug/hevd-www-ida-vuln-spotting.png" title="image_alt") }} After checking the address we passed and printing some kernel debug messages, the function copies the value dereferenced from `rbx` (which is the function @@ -35,7 +41,7 @@ written at the address pointed by `rdi`. Or better summarized in assembly - `rcx` holds the function first argument (see [[2](#related-links)] for a good reminder about calling conventions): -```text +```txt 0000000000015B89 mov r12, rcx [...] 0000000000015B95 call cs:__imp_ProbeForRead @@ -72,7 +78,7 @@ overwrite the current process' token by overwriting directly the `_SEP_TOKEN_PRIVILEGES` and for example, provide it with the `SeDebugPrivilege` allowing it to perform any further privileged operation on the system (naturally it is assumed here that we know the current process structure's address - through an -infoleak or else). Back in 2012, [@cesarcer](https://twitter.com/cesarcer){:target="_blank" class="fa fa-twitter"} covered this very situation in his Black Hat +infoleak or else). Back in 2012, {{ twitter(user="cesarcer") }} covered this very situation in his Black Hat presentation [Easy Local Windows Kernel Exploitation](https://web.archive.org/web/20160909195733/https://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf). Although this second way would allow to work around SMEP, for the sake of @@ -88,7 +94,7 @@ this table contains the service tables in use when processing system calls. In KD, we can reach it at with the following symbol: `nt!KeServiceDescriptorTable` -```text +```txt kd> dps nt!KeServiceDescriptorTable fffff802`f8b57a80 fffff802`f895ad00 nt!KiServiceTable fffff802`f8b57a88 00000000`00000000 @@ -108,7 +114,7 @@ But why `HalDispatchTable` in particular? Because we can call from userland the function [`NtQueryIntervalProfile`](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/profile/queryinterval.htm), that will in turn invoke `nt!KeQueryIntervalProfile` in the kernel, which to finally perform a `call` instruction to the address in `nt!HalDispatchTable[1]`: -```text +```txt kd> u nt!KeQueryIntervalProfile+0x9 l7 nt!KeQueryIntervalProfile+0x9: fffff802`f8cbc23d ba18000000 mov edx,18h @@ -154,14 +160,14 @@ pointers table located at `0xFFD00000` (on x86 and x64). Actually the range even before the Windows memory manager) during the boot process, it'll require known static addresses to map and store information collected about the hardware in memory. Researchers such as - [@d_olex](https://twitter.com/d_olex){:target="_blank" class="fa fa-twitter"} have explored this path as early as 2011 to use it as an exploit vector as Win7 +{{ twitter(user="d_olex") }} have explored this path as early as 2011 to use it as an exploit vector as Win7 SP1 used to have this section static and with Read/Write/Execute permission (although it exists on Windows 8 and up, it is "only" Read/Write) -![Windows 8.1 HAL section](/assets/images/win-kernel-debug/hevd-www-hal-interrupt.png) +{{ img(src="/img/win-kernel-debug/hevd-www-hal-interrupt.png" title="Windows 8.1 HAL section") }} __Note__: Looking for references about HAL interrupt table corruption, I came across this recent and fantastic -[blog post](https://labs.bluefrostsecurity.de/blog/2017/05/11/windows-10-hals-heap-extinction-of-the-halpinterruptcontroller-table-exploitation-technique/) by [@NicoEconomou](https://twitter.com/NicoEconomou){:target="_blank" class="fa fa-twitter"} that covers exactly this approach. I might dedicate a future post applying this technique to HEVD as this table is also an excellent target for WWW scenario. +[blog post](https://labs.bluefrostsecurity.de/blog/2017/05/11/windows-10-hals-heap-extinction-of-the-halpinterruptcontroller-table-exploitation-technique/) by {{ twitter(user="NicoEconomou") }} that covers exactly this approach. I might dedicate a future post applying this technique to HEVD as this table is also an excellent target for WWW scenario. # Building the exploit @@ -195,7 +201,7 @@ DeviceIoControl(hDevice, IOCTL_HEVD_ARBITRARY_OVERWRITE, lpBufferIn, sizeof(lpBu ``` And if we test with dummy values: -![exploit-test](/assets/images/win-kernel-debug/hevd-www-testing-exploit.png) +{{ img(src="/img/win-kernel-debug/hevd-www-testing-exploit.png" title="exploit-test") }} The `WHAT` corresponds to our shellcode (`lpShellcode`), which we know. Now we need the `WHERE` (i.e. `nt!HalDispatchTable[1]`)... which a kernel address! As we know, any @@ -264,7 +270,7 @@ NtQueryIntervalProfile(dummy1, &dummy2); The clean final exploit can be found [here](https://github.com/hugsy/hevd/blob/c04e46ababbb78913ef228c31389370f17d8e48a/ArbitraryOverwrite/exploit.c). -![image_alt](/assets/images/win-kernel-debug/hevd-www-final-exploit.png) +{{ img(src="/img/win-kernel-debug/hevd-www-final-exploit.png" title="image_alt") }} You can now enjoy the privileged shell so well deserved! @@ -282,7 +288,7 @@ Although PG bypass is not the subject of this post, it should be noted that # Conclusion -In this chapter we've covered how to exploit Arbitrary Write conditions in the kernel to achieve code execution, by leveraging undocumented procedures and functions that leak valuable kernel information straight from userland. Many more leaks exist, and I definitely recommend watching [@aionescu](https://twitter.com/aionescu){:target="_blank" class="fa fa-twitter"}'s REcon 2013 talk [I got 99 problems but a kernel pointer ain't one](https://www.youtube.com/watch?v=5HbmpPBKVFg). +In this chapter we've covered how to exploit Arbitrary Write conditions in the kernel to achieve code execution, by leveraging undocumented procedures and functions that leak valuable kernel information straight from userland. Many more leaks exist, and I definitely recommend watching {{ twitter(user="aionescu") }}. See you next time ✌ diff --git a/content/2017-10-13-flareon-4-writeups.md b/content/2017-10-13-flareon-4-writeups.md index 33c9f192..4b7cb4a5 100644 --- a/content/2017-10-13-flareon-4-writeups.md +++ b/content/2017-10-13-flareon-4-writeups.md @@ -1,10 +1,16 @@ -date: 2017-10-13 00:00:00 -modified: 2017-10-13 00:00:00 -title: FlareOn 4 WriteUps -author: hugsy -category: ctf -cover: assets/images/flareon-2017-header.png -tags: reverse,flareon,windows,pe,linux,elf,arduino,avr ++++ +title = "FlareOn 4 WriteUps" +authors = ["hugsy"] +date = 2017-10-13T00:00:00Z +updated = 2017-10-13T00:00:00Z + +[taxonomies] +categories = ["ctf"] +tags = ["reverse","flareon","windows","pe","linux","elf","arduino,avr"] + +[extra] +header_img = "/img/flareon-2017-header.png" ++++ This year, I happened to finally have a chance to be in a good position to play [Flare-On CTF](https://flare-on.com), a yearly CTF published by [FireEye](https://web.archive.org/web/20170831191227/https://www.fireeye.com/blog/threat-research/2017/08/fourth-annual-flare-on-challenge.html). This @@ -14,7 +20,7 @@ This post is mostly a dump of the notes taken during all the challenges. Link to challenges and scripts are also given. -# Menu # +# Menu For quick jump: @@ -27,7 +33,7 @@ All the challenges are in the ZIP file that you can [download here](https://mega.nz/#F!lVQzXZZQ!bZkK8Q2XkLb0O-RE-hCl1g). -# The Arsenal # +# The Arsenal My complete arsenal was (in no particular order): @@ -64,22 +70,22 @@ My complete arsenal was (in no particular order): And a lot of C and Python snippets... -# Challenge 1 # +# Challenge 1 -## Instruction ## +# Instruction -```text +```txt Welcome to the Fourth Flare-On Challenge! The key format, as always, will be a valid email address in the @flare-on.com domain. ``` -## Solution ## +# Solution By checking the [HTML source code](https://mega.nz/#!1EQhhLrT!uWOWRRGc-8Lx2D0iLxkSk3qMSK-xcWBV8Pnj8CYTaRg), we see: -![image_alt](/assets/images/flareon-2017/17161f3635f37c0b278c18262e4a29eb4f21675316ff9a086557e390ca3be67e.png) +{{ img(src="/img/flareon-2017/17161f3635f37c0b278c18262e4a29eb4f21675316ff9a086557e390ca3be67e.png" title="image_alt") }} Classic ROT-13, can be decoded by: @@ -92,16 +98,16 @@ ClientSideLoginsAreEasy@flare-on.com [Back to Menu](#menu) -# Challenge 2 # +# Challenge 2 -## Instruction ## +# Instruction -```text +```txt You solved that last one really quickly! Have you ever tried to reverse engineer a compiled x86 binary? Let's see if you are still as quick. ``` -## Solution ## +# Solution [`IgniteMe.exe`](https://mega.nz/#!gBIF0aYQ!82SWKCVa3hw2sI3f_2AsaHaoVwj2zux5ORXXfNMi2F4) is a small PE that reads @@ -109,7 +115,7 @@ what a buffer from stdin and chain-xor it in reverse (with an IV set to `4` by function at 0x00401000) and then compared to an `encoded_key` located at 0x0403000: -```text +```txt 00403000 0d 26 49 45 2a 17 78 44-2b 6c 5d 5e 45 12 2f 17 .&IE*.xD+l]^E./. 00403010 2b 44 6f 6e 56 09 5f 45-47 73 26 0a 0d 13 17 48 +DonV._EGs&....H 00403020 42 01 40 4d 0c 02 69 00 B.@M..i. @@ -127,25 +133,25 @@ result R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com [Back to Menu](#menu) -# Challenge 3 # +# Challenge 3 -## Instruction ## +# Instruction -``` +```txt Now that we see you have some skill in reverse engineering computer software, the FLARE team has decided that you should be tested to determine the extent of your abilities. You will most likely not finish, but take pride in the few points you may manage to earn yourself along the way. ``` -## Solution ## +# Solution [`greek_to_me`](https://mega.nz/#!4cpGWS5S!QCTrpXnC8q4WYnMHaxbqFA4mPDOVC4q2toAYGKSfe68) is a PE file that will start by binding and listen tcp/2222, and receive 4 bytes from the socket. This value read will be used to decode the instructions at 0x40107c to 0x4010ee: -![image_alt1](/assets/images/flareon-2017/489d77b797f222ef52533b5da295fd7e733c9156ec43cbd44aa1f8163ece1f81.png) +{{ img(src="/img/flareon-2017/489d77b797f222ef52533b5da295fd7e733c9156ec43cbd44aa1f8163ece1f81.png" title="image_alt1") }} Being lazy, I've reconstructed [this C script](https://gist.github.com/7c7ee0e9cd9399a5ec975a72cfe58486) from @@ -176,30 +182,30 @@ print s.recv(0x100) which will show as a response: -``` +```txt Congratulations! But wait, where's my flag? ``` But by setting WinDBG to break at 0x040107c and by passing the correct decoding key when prompted, a whole new code shows up: -![image_alt](/assets/images/flareon-2017/05d0733685c70aa9802ace1c97c240ace73a3c18c941219d975775cae32d10a5.png) +{{ img(src="/img/flareon-2017/05d0733685c70aa9802ace1c97c240ace73a3c18c941219d975775cae32d10a5.png" title="image_alt") }} Revealing the key to this level. [Back to Menu](#menu) -# Challenge 4 # +# Challenge 4 -## Instruction ## +# Instruction -``` +```txt You're using a VM to run these right? ``` -## Solution ## +# Solution This challenge was very fun at the beginning, but the last part really sucked: [`notepad.exe`](https://mega.nz/#!IZA3nbLK!qdpuFX29rpXHBfEdXRWMq5R-gHw-5QHiN9cAMhx2vsk) is a small PE that by all @@ -208,9 +214,9 @@ the instruction to this challenge, I expected a malware or something hostile, but it is nothing of the sort. Disassembling the `start` in IDA shows a bunch of interesting strings: -![image_alt](/assets/images/flareon-2017/c2be22c2350ecf3a792cfa07a72ee0c6a55e129e60642577e70994e53c3e2efd.png) +{{ img(src="/img/flareon-2017/c2be22c2350ecf3a792cfa07a72ee0c6a55e129e60642577e70994e53c3e2efd.png" title="image_alt") }} -``` +```txt %USERPROFILE%\flareon2016challenge ImageHlp.dll CheckSumMappedFile @@ -220,7 +226,7 @@ MessageBoxA So I created the folder `flareon2016challenge` and spawned `procmon`: -![image_alt](/assets/images/flareon-2017/9e3fc6079d951d311ad3bacdee5d98d5d191b63663d7803e93ec1f260cbde521.png) +{{ img(src="/img/flareon-2017/9e3fc6079d951d311ad3bacdee5d98d5d191b63663d7803e93ec1f260cbde521.png" title="image_alt") }} clearly showing that `notepad` is looking for something in this directory. Breaking @@ -232,7 +238,7 @@ a and calling the function at 0x1014E20 when a file is found. That's where stuff gets interesting. -![image_alt]/assets/images/flareon-2017/d9d6b730545915c4d7a94f05ff7b42ab7b5ba9fa5a9bc119147d6a35dd357c18.png) +![image_alt]/img/flareon-2017/d9d6b730545915c4d7a94f05ff7b42ab7b5ba9fa5a9bc119147d6a35dd357c18.png) `notepad` maps the file in memory, checks if it started with `MZ`, gets the value at offset 0x3c, then jump to @@ -248,7 +254,7 @@ of the current program (`notepad.exe`) and the PE file mapped to memory. If those 2 values are the ones expected, then 2 functions are called successively: 1. Function @ 0x1014350 which will format the timestamp of the mapped file and - `MessageBox`-it ![image_alt](/assets/images/flareon-2017/3321b96da80e52cd9e26eda05122bb1bd58a18216d6aeb1b4205162d2ed6dbf6.png) + `MessageBox`-it {{ img(src="/img/flareon-2017/3321b96da80e52cd9e26eda05122bb1bd58a18216d6aeb1b4205162d2ed6dbf6.png" title="image_alt") }} 1. Function @ 0x1014BAC which will open a file `key.bin` in `flareon2016challenge` folder and write 8 bytes from some offset in the mapped file into it. @@ -288,13 +294,12 @@ use. This guessing game made me lose too much time. The hint was to use 2016 PE files from last year's FlareOn challenge. In the many folders of -the [FlareOn3 archive](http://flare-on.com/files/Flare-On3_Challenges.zip) -(pass: flare), we could find several PE files whose timestamps match perfectly +the FlareOn3 Zip archive, we could find several PE files whose timestamps match perfectly with the ones we are looking for. All we need now is drop those files in the `flareon2016challenge` directory, and tweak `notepad.exe` to update its timestamp. After 4 executions we get the `key.bin` file properly filled: -``` +```bash ➜ xd ~/ctf/flareon_2017/4/key.bin 00000000 55 8b ec 8b 4d 0c 56 57 8b 55 08 52 ff 15 30 20 |U...M.VW.U.R..0 | 00000010 c0 40 50 ff d6 83 c4 08 00 83 c4 08 5d c3 cc cc |.@P.........]...| @@ -303,7 +308,7 @@ timestamp. After 4 executions we get the `key.bin` file properly filled: And after updating `notepad` to the last PE timestamp, we get: -![image_alt](/assets/images/flareon-2017/fe5e80d5dd81c1350413732f30ed5ba2b2e4ae1cf92b00504fa6a0bba1b9a820.png) +{{ img(src="/img/flareon-2017/fe5e80d5dd81c1350413732f30ed5ba2b2e4ae1cf92b00504fa6a0bba1b9a820.png" title="image_alt") }} [Back to Menu](#menu) @@ -312,19 +317,19 @@ And after updating `notepad` to the last PE timestamp, we get: # Challenge 5 -## Instruction ## +# Instruction -``` +```txt You're doing great. Let's take a break from all these hard challenges and play a little game. ``` -## Solution ## +# Solution [`pewpewboat.exe`](https://mega.nz/#!pdgDDITS!CCXq80gh7M2YxOosfdd_jKXG2N9uUSG_1_5NLY_rbFg) is not a PE file but an x64 ELF that starts a nice ASCII implementation of [the Battleship game](https://en.wikipedia.org/wiki/Battleship_(game)). -``` +```bash root@kali2:/ctf/flareon_2017/5 # ./pewpewboat.exe Loading first pew pew map... 1 2 3 4 5 6 7 8 @@ -349,13 +354,13 @@ The binary starts by initializing the PRNG with the current timestamp, then allocated a 0x240 in the heap, and starts populating it randomly. It then enters a loop of game, where the player (us) have 0x64 attempts to win the game. -![image_alt](/assets/images/flareon-2017/c1042765b377b68599461aa2c7fbabeb502f831a49db09cb5bb6223a22c99bce.png) +{{ img(src="/img/flareon-2017/c1042765b377b68599461aa2c7fbabeb502f831a49db09cb5bb6223a22c99bce.png" title="image_alt") }} Inside the loop, the function `play()` (at 0x4038d6) is called and will print the game grid and display whether your shot was hit or miss. The coordinates themselves are read from the function `enter_coor()` (at 0x40377d). -![image_alt](/assets/images/flareon-2017/aea95f918e61631fae4e6fe1d003951d1fc30d7fcf0e8ac787b14983e264c876.png) +{{ img(src="/img/flareon-2017/aea95f918e61631fae4e6fe1d003951d1fc30d7fcf0e8ac787b14983e264c876.png" title="image_alt") }} So if we want to win, we need to @@ -386,7 +391,7 @@ The function `draw_grid()` called with a pointer to the game board as parameter. By reading it, the function knows how to print a cell (empty, full) and therefore knows the configuration of the board. -``` +```txt gef➤ bp *0x403c3a gef➤ dps $rdi l1 0x0000000000614010│+0x00: 0x0008087808087800 ← $rax, $rdi @@ -439,7 +444,7 @@ By advancing through all the levels, we can collect more letters: Reaching the final level and entering the valid positions of boats gets a message: -``` +```txt Final answer: Aye! You found some letters did ya? To find what you're looking for, you'll want to re-order them: @@ -454,7 +459,7 @@ By simply applying this formula, we find the result to be `ohgjurervfgurehz` which when in uppercase ROT13-ed gives `BUTWHEREISTHERUM`. Give this password as input, and after a bit of computation time obtain the key to finish the level: -![image_alt](/assets/images/flareon-2017/532e605c764a754f32dbb0d2581913dbf0283d76e21f12cbf92841cfae67f8c4.png) +{{ img(src="/img/flareon-2017/532e605c764a754f32dbb0d2581913dbf0283d76e21f12cbf92841cfae67f8c4.png" title="image_alt") }} [Back to Menu](#menu) @@ -462,15 +467,15 @@ input, and after a bit of computation time obtain the key to finish the level: # Challenge 6 -## Instruction ## +# Instruction -``` +```txt I hope you enjoyed your game. I know I did. We will now return to the topic of cyberspace electronic computer hacking and digital software reverse engineering. ``` -## Solution ## +# Solution [`payload.dll`](https://mega.nz/#!Nd5g3ToA!ArZp4KMqteCSQQwywP2LE-xdYly-UoQEBoig4CfCuIY) is a PE32+ DLL x86-64. The DLL doesn't sweat much info out of the box, so I decide to use both dynamic and @@ -554,20 +559,20 @@ Using the loader, we can now invoke this function easily: Which when compiled and executed triggers to display the following MessageBox: -![image_alt](/assets/images/flareon-2017/eaca6198b81df65f296bc6d280437944ee7745fae6c9168d2500b12d0a5c1345.png) +{{ img(src="/img/flareon-2017/eaca6198b81df65f296bc6d280437944ee7745fae6c9168d2500b12d0a5c1345.png" title="image_alt") }} We get one letter of the key! Good start, but how could we get more? And why do we get the 26th character? To know that we must understand the function 0x180005D30: -![image_alt](/assets/images/flareon-2017/a686c1bd8e99734457a6cec1549cfdb8218e5ebaa9e62e412110bb9a9062508e.png) +{{ img(src="/img/flareon-2017/a686c1bd8e99734457a6cec1549cfdb8218e5ebaa9e62e412110bb9a9062508e.png" title="image_alt") }} This function gets a pointer to the [Export Directory table](http://resources.infosecinstitute.com/the-export-directory/) then calls the function 0x180004710: -``` +```asm .text:000000018000471E mov [rsp+48h+var_18], rax .text:0000000180004723 lea rcx, [rsp+48h+SystemTime] ; lpSystemTime .text:0000000180004728 call cs:GetSystemTime @@ -645,7 +650,8 @@ CallWithArgs(addr, p1, p2, p3, p4); ``` That will print out successively the key parts via successive `MessageBox` calls. -``` + +```txt 0x77, 0x75, 0x75, 0x75, 0x74, 0x2d, 0x65, 0x78, 0x70, 0x30, 0x72, 0x74, 0x73, 0x40, 0x66, 0x6c, 0x61, 0x72, 0x65, 0x2d, 0x6f, 0x6e, 0x2e, 0x63, ``` @@ -659,14 +665,14 @@ which translated gives `wuuut-exp0rts@flare-on.com` # Challenge 7 -## Instruction ## +# Instruction -``` +```txt I want to play another game with you, but I also want you to be challenged because you weren't supposed to make it this far. ``` -## Solution ## +# Solution [`zsud.exe`](https://mega.nz/#!BQIlHJ6Z!_-qOpHyiXaZqq2CV_o42du5blCGmkzrlJKrXs6WG2oU) is a PE32 binary. Running @@ -675,7 +681,7 @@ because you weren't supposed to make it this far. 1. this binary is C# compiled 1. it embeds a DLL -``` +```bash $ binwalk zsud.exe DECIMAL HEXADECIMAL DESCRIPTION @@ -693,7 +699,7 @@ AES decrypted with the key "`soooooo_sorry_zis_is_not_ze_flag`". The result is a Powershell script that is being invoked, and that is another maze game, entirely written in Powershell. The script can be downloaded [here](https://gist.github.com/750558c5ed49c291e50dc460821e8e09). -![image_alt](/assets/images/flareon-2017/090d3fe35ac25fcec9052f0e216f72c75da6d96a367abe4451d04ff0af7ad5cd.png) +{{ img(src="/img/flareon-2017/090d3fe35ac25fcec9052f0e216f72c75da6d96a367abe4451d04ff0af7ad5cd.png" title="image_alt") }} The game is an escape room, so it would make sense that the flag will be given to us if we escape! And since it's a maze, we need to find the proper @@ -741,7 +747,7 @@ while True: ``` And we start getting the beginning of the path: -![image_alt](/assets/images/flareon-2017/304507dabd5f847b7beafec89b19e225540db7649cedfc0e2ebe4703df14a06b.png) +{{ img(src="/img/flareon-2017/304507dabd5f847b7beafec89b19e225540db7649cedfc0e2ebe4703df14a06b.png" title="image_alt") }} ```python directions ='wnneesssnewne' @@ -810,7 +816,7 @@ $helmet = 1; Then execute only this portion of code to see: -![image_alt2](/assets/images/flareon-2017/dededfb9d354408d37fab58d50b62856c51ef5a3326ab05a42470df936f6dbf1.png) +{{ img(src="/img/flareon-2017/dededfb9d354408d37fab58d50b62856c51ef5a3326ab05a42470df936f6dbf1.png" title="image_alt2") }} Which unhexlified gives the flag: @@ -825,15 +831,15 @@ mudd1ng_by_y0ur53lph@flare-on.com # Challenge 8 -## Instruction ## +# Instruction -``` +```txt You seem to spend a lot of time looking at your phone. Maybe you would finish a mobile challenge faster. I want to play another game with you, but I also want you to be challenged because you weren't supposed to make it this far. ``` -## Solution ## +# Solution This really fun challenge offers an Android APK file, [`flair.apk`](https://mega.nz/#!xFoXkTRa!L3h7J_copL4NuA3pEW0bR5Acrz7LeLXVFTV2sb_Ha08). The static analysis was @@ -841,7 +847,7 @@ exclusively done with JADX and I used the awesome GenyMotion + JDB for the dynam This app presents itself as a traditional Android app, `com.flare_on.flair`: -![image_alt](/assets/images/flareon-2017/638b63ff20d2447bdcf9ca2f7dbf3e9a8800178722580185a0c9c7f86652f707.png) +{{ img(src="/img/flareon-2017/638b63ff20d2447bdcf9ca2f7dbf3e9a8800178722580185a0c9c7f86652f707.png" title="image_alt") }} You can get the final flag by solving the 4 mini challenges: @@ -855,7 +861,7 @@ You can get the final flag by solving the 4 mini challenges: Using `JADX`, we can reach easily the method `simply solve com.flare_on.flair.Michael.checkPassword()`: -![image_alt](/assets/images/flareon-2017/ad3a22fa907e8c8185c87b256bfc4fa542c68eb5dfcc508d4ea8620adab9d859.png) +{{ img(src="/img/flareon-2017/ad3a22fa907e8c8185c87b256bfc4fa542c68eb5dfcc508d4ea8620adab9d859.png" title="image_alt") }} Which trivially gives us the first answer: `MYPRSHE__FTW` @@ -868,7 +874,7 @@ app. JADX shows that when the validation button is clicked on, the method function is a simple `memcmp()`-like function, so we can break on it and dump its arguments: -``` +```bash $ jdb -attach localhost:8700 > methods com.flare_on.flair.Brian [...] @@ -943,7 +949,7 @@ And we get: And finally: -![image_alt](/assets/images/flareon-2017/7ecf50b91265cdd05f48d3910c20c9d48899a3e19645fbe263f8c34a696d00cc.png) +{{ img(src="/img/flareon-2017/7ecf50b91265cdd05f48d3910c20c9d48899a3e19645fbe263f8c34a696d00cc.png" title="image_alt") }} @@ -954,16 +960,16 @@ And finally: -## Instruction ## +# Instruction -``` +```txt One of our computer scientists recently got an Arduino board. He disappeared for two days and then he went crazy. In his notebook he scrawled some insane jibberish that looks like HEX. We transcribed it, can you solve it? ``` -## Solution ## +# Solution The challenge is in a text file named [`remorse.ino.hex`](https://mega.nz/#!NFQwXKYQ!OhtgRSr6U4yRBMnflhIwGgMZJXYaEeMnJG-1m0bWFJ4). This format @@ -990,19 +996,19 @@ $ obj-x86_64-linux-gnu/simduino.elf -d -v -v ../../../remorse.ino.hex Simduino will open a /dev/pts that can be used for UART (so we can use tools like `picocom` or `minicom` to debug it). -![image_alt](/assets/images/flareon-2017/cd4cd292a48fa1fc086b50e5617459edec3e9d40513de244bf57428f0c372348.png) +{{ img(src="/img/flareon-2017/cd4cd292a48fa1fc086b50e5617459edec3e9d40513de244bf57428f0c372348.png" title="image_alt") }} The firmware seems to be expecting a new PIN configuration: luckily I came across this information in the datasheet ("35. Register Summary"). -![image_alt](/assets/images/flareon-2017/33d2e78e17819a705d01a9c9c0412090361e7ad02beb4692106996ac8e832f7b.png) +{{ img(src="/img/flareon-2017/33d2e78e17819a705d01a9c9c0412090361e7ad02beb4692106996ac8e832f7b.png" title="image_alt") }} After trying to manipulate the PINB and PINC (resp. at offset 0x23 and 0x26) without success, I saw that a change of value in PIND (offset 0x29) immediately provoked a response from the firmware: -``` +```bash $ avr-gdb -q -ex 'target remote localhost:1234' [...] (gdb) set {char}0x29=0 @@ -1032,7 +1038,7 @@ $ i=0; while [ $i -lt 256 ]; do sleep 5 ; xdotool key ctrl+c Up Return ; i=$((i Went for a coffee, and when back saw the pleasant screen: -![image_alt3](/assets/images/flareon-2017/72de36af7ebcf629992d8b5f9f3a54e20cb01d6335fd961984d34b0840ea4b7e.png) +{{ img(src="/img/flareon-2017/72de36af7ebcf629992d8b5f9f3a54e20cb01d6335fd961984d34b0840ea4b7e.png" title="image_alt3") }} This challenge was a good reminder that reading the documentation first kept me from spending probably hours of not understanding how the CPU was getting @@ -1044,16 +1050,16 @@ input/output data from the PIN or what the ABI was doing. So more than ever, RTF # Challenge 10 -## Instruction ## +# Instruction -``` +```txt We have tested you thoroughly on x86 reversing but we forgot to cover some of the basics of other systems. You will encounter many strange scripting languages on the Information Superhighway. I know that Interweb challenges are easy, but we just need you to complete this real quick for our records. ``` -## Solution ## +# Solution Another guessing game type of challenge. The challenge comes as a PHP script named [`shell.php`](https://mega.nz/#!MUAWhDTQ!qzAe4c6O0ADp3YyfCNVF0gimNSs44kvpLWwqcoldoKs). It was solvable in 3 different steps: @@ -1076,7 +1082,7 @@ line 15, we know that the block once de-XOR-ed will have all bytes in Now the guessing game starts: we must guess at the same time the length and the key. So the idea is in pseudo-code -``` +```txt assuming len(key) = 32 assuming charset = "0123456789abcdef" let candidate = (key[0], len(32)) @@ -1125,7 +1131,7 @@ chunk. This will be the main idea: -``` +```python possible_candidates = {0: "abc", 1: "012", 2: "f", etc...} possible_block = [] block_size = 4 # pure assumption @@ -1140,7 +1146,7 @@ for candidate in generate_all_candidates( possible_candidates[0:block_size] ): I used Python's `itertools.product` to generate all the candidate blocks, and little by little recovered the value for `$param`: -``` +```bash $ python bf2.py possible_key=de6952b84a49b934acb436418ad9d93d237df05769afc796d063000000000000 (0, '$c=\'\';\r\n$key = "";\r\nif (isset($_POST[\'o_o\']))\r\n $ka') @@ -1193,7 +1199,7 @@ Just like step1 this approach gives us 2 possible length for the flag prefix So there again, semi-manual brute-force: -``` +```python i = 9 k0 = key[0::3] for t in string.printable: @@ -1207,7 +1213,7 @@ for t in string.printable: We quickly notice that the output has some HTML in it, so we can discard candidates with invalid HTML patterns. For example: -``` +```bash ➜ python bf.py AAAAAAAA0froc ['8titl', 'ged C', '`\r'] AAAAAAAA2froc [':titl', 'eed C', 'b\r'] @@ -1225,7 +1231,7 @@ so forth. Reiterating this for all bytes, we get the first subkey to be And reiterating the exact same thing for the 2nd and 3rd base64-encoded block and we get all the subkeys: -``` +```python >>> k0='t_rsaat_4froc' >>> k1='hx__ayowkleno' >>> k2='3Oiwa_o3@a-.m' @@ -1240,15 +1246,14 @@ and we get all the subkeys: # Challenge 11 -## Instruction ## +# Instruction -``` +```txt Only two challenges to go. We have some bad hombres here but you're going to get the keys out. - ``` -## Solution ## +# Solution This challenge was out of space! And so fun! It comes as a PE32 file named [`covfefe.exe`](https://mega.nz/#!EdIHXLxD!ctm5aE88lVss0EafshM0APMebGDSjhEcXajC6F8GVYc). @@ -1258,7 +1263,7 @@ from the PE points us nostalgically to Rick Astley timeless masterpiece, "Never Gonna Give You Up". Many other strings appear, but are weirdly aligned to one DWORD per character: -![image_alt](/assets/images/flareon-2017/a0e353204c9ddbd73d9a71c3c6ec53ba7c068d4ab487d43726ebfbe66aef3e8b.png) +{{ img(src="/img/flareon-2017/a0e353204c9ddbd73d9a71c3c6ec53ba7c068d4ab487d43726ebfbe66aef3e8b.png" title="image_alt") }} Actually `covfefe.exe` is very simple, and only asks for finding a correct password. The PE itself only: @@ -1272,7 +1277,7 @@ The VM is an array of `int32_t` so The execution of the virtual machine starts at `pc_start = vm + 0x463`. And each instruction is executed in the same way: -``` +```txt execute_instruction(operand1, operand2, operand3) { [operand2] = [operand2] - [operand1] if [operand2] <= 0 && operand3 != -1: @@ -1283,7 +1288,7 @@ execute_instruction(operand1, operand2, operand3) { Since the code is super easy, I decided to recreate the C source code from it. So first, I used WinDBG to dump the VM location: -``` +```txt 0:000> .writemem F:\flareon_2017\11\dumpmem-00403000-L5000.dmp ``` @@ -1298,24 +1303,24 @@ reason for that being that it greatly helped tracking down operations at the bytecode level of the VM. -![image_alt](/assets/images/flareon-2017/202dd92a07c692ff036fd5b27d7ff1c85f1af93cd33007abf2fb31bd44498270.png) +{{ img(src="/img/flareon-2017/202dd92a07c692ff036fd5b27d7ff1c85f1af93cd33007abf2fb31bd44498270.png" title="image_alt") }} We know that we must provide a good password to validate the task. So there must be a comparison that fails as soon as a wrong character is entered. Those new tools were of great help to identify the culprit: the comparison instruction is done in the block at 0xde6. -![image_alt](/assets/images/flareon-2017/a25240fe0264b71f12bf0371e663fe5357dd0b9f6366056b34814a5bd2670e2b.png) +{{ img(src="/img/flareon-2017/a25240fe0264b71f12bf0371e663fe5357dd0b9f6366056b34814a5bd2670e2b.png" title="image_alt") }} Now that we know that, all I need was to use the C script to "set a breakpoint" at 0xde9 and see what value was expected. -![image_alt](/assets/images/flareon-2017/dc8897ca8ce6dc0a124da94b1e7e7ddf7fc442b137930a003c31875b547c3ec9.png) +{{ img(src="/img/flareon-2017/dc8897ca8ce6dc0a124da94b1e7e7ddf7fc442b137930a003c31875b547c3ec9.png" title="image_alt") }} Knowing this, creating the brute-force script ([`cov.py`](https://gist.github.com/hugsy/12ffb0aaacbf87db3247ad1a07acb13c#file-cov-py)) was the next immediate step: -![image_alt5](/assets/images/flareon-2017/5afbab3abc4ad96ab713f58c496eaee64e2efb5ae92760a084c6f5cf55a90caa.png) +{{ img(src="/img/flareon-2017/5afbab3abc4ad96ab713f58c496eaee64e2efb5ae92760a084c6f5cf55a90caa.png" title="image_alt5") }} And finally recover the key to this level = `subleq_and_reductio_ad_absurdum`. @@ -1325,9 +1330,9 @@ And finally recover the key to this level = `subleq_and_reductio_ad_absurdum`. # Challenge 12 -## Instruction ## +# Instruction -``` +```txt Sorry, we don't have a challenge for you. We were hacked and we think we lost it. Its name was "lab10" . The attacker left one binary behind and our sophisticated security devices captured network traffic (pcap) that may be @@ -1335,7 +1340,7 @@ related. If you can recover the challenge from this and solve it then you win the Flare-On Challenge. If you can't then you do not win it. ``` -## Solution ## +# Solution This level alone could have been an entire CTF. It came as 2 files: @@ -1351,12 +1356,12 @@ stager to download and execute the real payload. Using API Monitor, we can trace that it attempts to connect to FQDN `maybe.suspicious.to`, checking also that the domain name doesn't point to the localhost -![image_alt](/assets/images/flareon-2017/c1de5ea5895e4bb38d54167604de4dff8c75dd14d757d40b1d1992419d085232.png) +{{ img(src="/img/flareon-2017/c1de5ea5895e4bb38d54167604de4dff8c75dd14d757d40b1d1992419d085232.png" title="image_alt") }} The behavior seems consistent with the first TCP stream of the PCAP. However, the data received seems encoded/encrypted: -``` +```txt GET /secondstage HTTP/1.1 Accept: */* Accept-Language: en-us @@ -1366,7 +1371,7 @@ Cache-Control: no-cache HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/2.7.12 -Date: Tue, 01 Aug 2017 17:04:02 GMT +date = Tue, 01 Aug 2017 17:04:02 GMT Content-type: application/octet-stream Content-Length: 119812 Last-Modified: Tue, 01 Aug 2017 14:46:13 GMT @@ -1384,12 +1389,12 @@ performs sequentially the following operations: 1. decode the buffer, recovering a valid PE file, `secondstage.exe` 1. invoke `secondstage.exe` by [hollowing](https://www.trustwave.com/Resources/SpiderLabs-Blog/Analyzing-Malware-Hollow-Processes/) the default HTTP browser -![image_alt](/assets/images/flareon-2017/7d5697ef3325169816f81bd29388f6575c6dd51d23d9fcf11c26dc778f29b354.png) +{{ img(src="/img/flareon-2017/7d5697ef3325169816f81bd29388f6575c6dd51d23d9fcf11c26dc778f29b354.png" title="image_alt") }} Instead of decoding manually the encoded response from the C2 server, we can be lazy by recovering `secondstage.exe` breaking at 0x4104C1: -``` +```txt 0:000> bp 0x4104C1; g Breakpoint 0 hit [...] @@ -1427,9 +1432,9 @@ ensuring a bit of obfuscation during static analysis, since all function calls will be performed by indirect calls. Then if the executable is run on client-side, initiates the connection to the C2 server: -![image_alt](/assets/images/flareon-2017/ae6aff7a8232b182109f61df0cd50bf78f7ce4a5f162c8c16a5591b5f0f7aecc.png) +{{ img(src="/img/flareon-2017/ae6aff7a8232b182109f61df0cd50bf78f7ce4a5f162c8c16a5591b5f0f7aecc.png" title="image_alt") }} -![image_alt](/assets/images/flareon-2017/a06c9b431092bbd3e382f3d703dbe4828d0702f0140ee009aac3b341c145c32e.png) +{{ img(src="/img/flareon-2017/a06c9b431092bbd3e382f3d703dbe4828d0702f0140ee009aac3b341c145c32e.png" title="image_alt") }} Every time a packet is received the function 0x0402C50 is called for parsing the new message, and sending the answer back. The C2 is still behind the FQDN @@ -1457,7 +1462,7 @@ packet is received, the function ensures that its length is at least 0x24 bytes, and that the first 4 bytes are equal to "2017". This will be the aspect of the first 0x24 bytes of header: -``` +```txt 0000 "2017" 0004 DataCheckSum 0008 HeaderSize @@ -1477,7 +1482,7 @@ the instruction `004053CE cmp edx, 4D4Ch`, which happens to be followed by a call to `Kernel32!VirtualAlloc()` with `PAGE_EXECUTE_READWRITE` (0x40) set for permission, then a `LoadLibraryA`. This must be it, so we can now use WinDBG to dump all those modules: -``` +```txt 0:000> bp 004053ce ; g 0:000> dd ecx+poi(ecx+3c)+50 l1 0018d2b8 00017000 @@ -1489,7 +1494,7 @@ Writing 17000 bytes.............................................. replacing "LM\x00\x00" with "MZ\x00\x00", and "NOP\x00" with "PE\x00\x00". Finally the entry point must be xored with the value 0xABCDABCD. -![image_alt](/assets/images/flareon-2017/bbcda00a98ff78d846bfa7a6e2b0e846cdcd50a8cc7cd8b4b4a8b79f4a1b49db.png) +{{ img(src="/img/flareon-2017/bbcda00a98ff78d846bfa7a6e2b0e846cdcd50a8cc7cd8b4b4a8b79f4a1b49db.png" title="image_alt") }} ### Reversing the "Loadable Modules" ### @@ -1559,7 +1564,7 @@ parse the data of the PCAP (the final version of the parser can be [found here]( ### Reconstructing the screen capture ### -![image_alt](/assets/images/flareon-2017/18a58c8dbdd8b039dc0b8492474e2ae4c0180ecc2e88a26f2d5708059aee9d4b.png) +{{ img(src="/img/flareon-2017/18a58c8dbdd8b039dc0b8492474e2ae4c0180ecc2e88a26f2d5708059aee9d4b.png" title="image_alt") }} `m.dll` captures the desktop as a bitmap and send the raw data back to the C2 (uses the same function as @@ -1569,7 +1574,7 @@ because it is a pure bitmap, there is no information of the dimensions of the image. In addition, the image is split in several packets, some of them are sent in plain text, like this -``` +```txt 00010A26 32 30 31 37 49 d8 69 59 24 00 00 00 4c 40 00 00 2017I.iY $...L@.. 00010A36 4c 40 00 00 51 29 8f 74 16 67 d7 ed 29 41 95 01 L@..Q).t .g..)A.. 00010A46 06 f5 05 45 1c 00 00 00 30 40 00 00 30 40 00 00 ...E.... 0@..0@.. @@ -1590,7 +1595,7 @@ by alex_k_polyakov, I used the website [RawPixels.net](http://rawpixels.net), and when setting a resolution of 1420x720, the following capture showed up: -![image_alt6](/assets/images/flareon-2017/018ab4320dc95fa3b751227369cd27f7ee759579323d695c2453bcf9966179e0.png) +{{ img(src="/img/flareon-2017/018ab4320dc95fa3b751227369cd27f7ee759579323d695c2453bcf9966179e0.png" title="image_alt6") }} After all those efforts, finally a good lead on the challenge to find. @@ -1599,14 +1604,14 @@ After all those efforts, finally a good lead on the challenge to find. Continuing the replay of packets showed something very interesting: -![image_alt](/assets/images/flareon-2017/01c609d44427749a2caa64d7cb8ae54f41788be7313e2c94fd9cd8f65476cc9c.png) +{{ img(src="/img/flareon-2017/01c609d44427749a2caa64d7cb8ae54f41788be7313e2c94fd9cd8f65476cc9c.png" title="image_alt") }} `secondstage.exe` was sending commands to a child process `cmd.exe`, attempting to reach a host whose NetBIOS name is `larryjohnson-pc`, and if found, would run drop 2 files in `C:\staging`, `pse.exe` and `srv2.exe`. Finally it would execute the command: -``` +```bat pse.exe \\larryjohnson-pc -i -c -f -d -u larry.johnson -p n3v3rgunnag1veUup -accepteula srv2.exe ``` @@ -1645,9 +1650,9 @@ leading to not be able to decrypt files later on... But after some long hours perfecting the decrypting script, [the result](https://gist.github.com/hugsy/9b141827b66843ebbabc183731649f53#file-level12-py) pays off directly, and all traffic is now in plain text, revealing some crispy information: -![image_alt](/assets/images/flareon-2017/0d1da3b02573a0f2c451b9cf801355666639e4454e26ea138b1836bdd969b36e.png) +{{ img(src="/img/flareon-2017/0d1da3b02573a0f2c451b9cf801355666639e4454e26ea138b1836bdd969b36e.png" title="image_alt") }} -![image_alt](/assets/images/flareon-2017/54e0782509ce641e04edd2b4bb2fef3d80f31c6640451952464ff9d50b5cb851.png) +{{ img(src="/img/flareon-2017/54e0782509ce641e04edd2b4bb2fef3d80f31c6640451952464ff9d50b5cb851.png" title="image_alt") }} 2 new files can be found from the extract: @@ -1657,12 +1662,12 @@ directly, and all traffic is now in plain text, revealing some crispy informatio `cf.exe` doesn't show much mystery: it takes 2 parameters, a path to file, and a base64 encoded key. And it will AES encrypt the file with the given key. -![image_alt](/assets/images/flareon-2017/0fbe4ce9c0d1295088fa6938b36081272c976a99ca80fef5f27ec3c89ea0cafb.png) +{{ img(src="/img/flareon-2017/0fbe4ce9c0d1295088fa6938b36081272c976a99ca80fef5f27ec3c89ea0cafb.png" title="image_alt") }} As seen in the capture above, we were capable of decrypting the packet that holds the command used for encrypting the file. -``` +```bat c:\staging\cf.exe lab10.zip tCqlc2+fFiLcuq1ee1eAPOMjxcdijh8z0jrakMA/jxg= ``` @@ -1706,7 +1711,7 @@ if __name__ == "__main__": open("lab10.zip", "wb").write(decrypted_file_content) ``` -``` +```bash $ python uf.py crypfile [+] data_size = 0x89334 [+] iv: fec85f816b82806996fc991b5731d2e1 @@ -1729,7 +1734,7 @@ The answer is: 'n3v3r_gunna_l3t_you_down_1987_4_ever@flare-on.com' ``` -# Conclusion # +# Conclusion Thank you to FireEye for those fun challenges... and congratulations to all the winners (especially those who managed to finish in under a week, massive @@ -1744,6 +1749,6 @@ challenge 12, it was **huge** in all the possible meanings, and it must certainly have required a serious patience to build! And final thanks to alex_k_polyakov, - n4x0r31 and @aymansagy. + n4x0r31 and {{ twitter(user="aymansagy") }}. See you next year for Flare-On 5! diff --git a/content/2018-01-07-building-a-debian-stretch-qemu-image-for-aarch64.md b/content/2018-01-07-building-a-debian-stretch-qemu-image-for-aarch64.md index 1aa807b8..d50cc409 100644 --- a/content/2018-01-07-building-a-debian-stretch-qemu-image-for-aarch64.md +++ b/content/2018-01-07-building-a-debian-stretch-qemu-image-for-aarch64.md @@ -1,10 +1,16 @@ -date: 2018-01-07 00:00:00 -modified: 2018-01-07 00:00:00 -title: Building a Debian Stretch QEMU image for AARCH64 -author: hugsy -category: tutorial -tags: gef,qemu,aarch64 -cover: assets/images/qemu-img.png ++++ +title = "Building a Debian Stretch QEMU image for AARCH64" +authors = ["hugsy"] +date = 2018-01-07T00:00:00Z +updated = 2018-01-07T00:00:00Z + +[taxonomies] +categories = ["tutorial"] +tags = ["gef","qemu","aarch64"] + +[extra] +header_img = "/img/qemu-img.png" ++++ ## Introduction @@ -16,8 +22,7 @@ If you're just interested in downloading the ready-to-use AARCH64 image, just go ## Pre-requisite -Just like [we did earlier in the former post](), we will proceed with the Debian -Net Installer, so you will require: +Just like [we did earlier in the former post](https://blahcat.github.io/posts/2017/07/14/building-a-debian-stretch-qemu-image-for-mipsel.html), we will proceed with the Debian Net Installer, so you will require: - an Internet connection - a recent QEMU (generally `{apt,dnf} install qemu` will suffice) @@ -42,7 +47,9 @@ $ qemu-img create -f qcow2 disk.qcow2 20G ## Installation steps -
 Note: since most steps are similar with the ones described in the post before, I'll simply show the commands I've used so they can be copy/pasted for reproduction.
+{% note() %} +since most steps are similar with the ones described in the post before, I'll simply show the commands I've used so they can be copy/pasted for reproduction. +{% end %} Start with running the installer (with 2 vCPUs and 1GB Ram): @@ -60,15 +67,15 @@ $ qemu-system-aarch64 -smp 2 -M virt -cpu cortex-a57 -m 1G \ ``` -![1.debian.installer.png](https://i.imgur.com/PAExOmJ.png) +{{ img(src="https://i.imgur.com/PAExOmJ.png" title="1.debian.installer.png") }} Then, go grab a coffee while the installer does its magic: -![2.debian.installer.png](https://i.imgur.com/1Mgoscl.png) +{{ img(src="https://i.imgur.com/1Mgoscl.png" title="2.debian.installer.png") }} And finally: -![3.debian.installer.png](https://i.imgur.com/IfvQpTC.png) +{{ img(src="https://i.imgur.com/IfvQpTC.png" title="3.debian.installer.png") }} Now we must shutdown the VM, and extract the initrd and kernel from the image, as follow: @@ -104,7 +111,7 @@ $ qemu-system-aarch64 -smp 2 -M virt -cpu cortex-a57 -m 1G \ And that's it! -![4.debian.installer.png](https://i.imgur.com/519SOdy.png) +{{ img(src="https://i.imgur.com/519SOdy.png" title="4.debian.installer.png") }} The ready-to-use image (with gcc, gdb, gef, etc.) is available [here](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA). diff --git a/content/2018-03-11-fuzzing-arbitrary-functions-in-elf-binaries.md b/content/2018-03-11-fuzzing-arbitrary-functions-in-elf-binaries.md index f47e16de..d66642e8 100644 --- a/content/2018-03-11-fuzzing-arbitrary-functions-in-elf-binaries.md +++ b/content/2018-03-11-fuzzing-arbitrary-functions-in-elf-binaries.md @@ -1,15 +1,21 @@ -date: 2018-03-11 00:00:00 -modified: 2018-03-11 00:00:00 -title: Fuzzing arbitrary functions in ELF binaries -author: hugsy -cover: assets/images/libfuzzer-lief/header.png -category: research -tags: fuzzing,elf,lief,libfuzzer,cve-2018-6789,exim ++++ +title = "Fuzzing arbitrary functions in ELF binaries" +authors = ["hugsy"] +date = 2018-03-11T00:00:00Z +updated = 2018-03-11T00:00:00Z + +[taxonomies] +categories = ["research"] +tags = ["fuzzing","elf","lief","libfuzzer","cve-2018-6789","exim"] + +[extra] +header_img = "/img/libfuzzer-lief/header.png" ++++ I decided to give a descent test to the [LIEF](https://lief-project.github.io/) project. Executable parsers are -[not](https://github.com/eliben/pyelftools) [a new](https://github.com/erocarrera/pefile) [thing]() but -that one picked my curiosity (just like most Quarkslab projects) because it +not a new thing ([`pyelftools`](https://github.com/eliben/pyelftools), [`pefile`](https://github.com/erocarrera/pefile), etc...) +but that one picked my curiosity (just like most Quarkslab projects) because it also provides dead simple instrumentation functions. To top it up, LIEF is easy to use and well documented, which is becoming a rare perk in the circus of infosec tools. @@ -48,14 +54,14 @@ realized this technique was relevant to dig into, so I chose to put it to practice by trying to find real vulnerabilities. -## Concrete example: finding CVE-2018-6789 ## +# Concrete example: finding CVE-2018-6789 What better way to illustrate this technique than with a concrete example: earlier this -week, mehqq_ released [a great blog post about CVE-2018-6789](https://devco.re/blog/2018/03/06/exim-off-by-one-RCE-exploiting-CVE-2018-6789-en/) detailing the exploit steps for an off-by-one vulnerability she discovered in Exim. The issue was fixed in [cf3cd306062a08969c41a1cdd32c6855f1abecf1](https://github.com/Exim/exim/commit/cf3cd306062a08969c41a1cdd32c6855f1abecf1) and given the CVE 2018-6789. +week, {{ twitter(user="mehqq_") }} released [a great blog post about CVE-2018-6789](https://devco.re/blog/2018/03/06/exim-off-by-one-RCE-exploiting-CVE-2018-6789-en/) detailing the exploit steps for an off-by-one vulnerability she discovered in Exim. The issue was fixed in [cf3cd306062a08969c41a1cdd32c6855f1abecf1](https://github.com/Exim/exim/commit/cf3cd306062a08969c41a1cdd32c6855f1abecf1) and given the CVE 2018-6789. [Exim](https://github.com/Exim/exim) is a MTA which once compiled is a standalone binary. So AFL would be of little help (network service), but it is a perfect practice case for LIEF + LibFuzzer. -We must compile Exim as PIE (usually done with setting `-fPIC` in CFLAGS and `-pie` in `LDFLAGS`). But we also need the [address sanitizer]() since without them, off-by-one overflow in the heap may go unoticed. +We must compile Exim as PIE (usually done with setting `-fPIC` in CFLAGS and `-pie` in `LDFLAGS`). But we also need the [address sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) since without them, off-by-one overflow in the heap may go unoticed. ### Compiling the target with ASAN & PIE ### @@ -75,7 +81,9 @@ $ FULLECHO='' LFLAGS+="-L/usr/lib/llvm-6.0/lib/clang/6.0.0/lib/linux/ -lasan -pi LIBS+="-lasan -pie" make -e clean all ``` -
 Note: in some cases, the use of ASAN fails to create the configuration file required
+{% note() %} +in some cases, the use of ASAN fails to create the configuration file required +{% end %} for the compilation. So edit `$EXIM/src/scripts/Configure-config.h` shell script to avoid the premature ending: @@ -93,7 +101,7 @@ index 75d366fc..a82a9c6a 100755 # message. Ensure that a broken config.h gets deleted. ``` -The compilation will occur normally and once compiled we can use `checksec` from [pwntools]() on the binary and make +The compilation will occur normally and once compiled we can use `checksec` from [pwntools](https://docs.pwntools.com/en/stable/) on the binary and make sure it's PIE and ASAN compatible: ```bash @@ -110,7 +118,7 @@ $ checksec ./build-Linux-x86_64/exim ### Exporting the targeted functions ### From the write-up, the vulnerable function is `b64decode()` in `src/base64.c` -whose [prototype](https://github.com/Exim/exim/blob/38e3d2dff7982736f1e6833e06d4aab4652f337a/src/src/base64.c#L152-L153){:target="_blank"} is: +whose [prototype](https://github.com/Exim/exim/blob/38e3d2dff7982736f1e6833e06d4aab4652f337a/src/src/base64.c#L152-L153) is: ```c int b64decode(const uschar *code, uschar **ptr) @@ -188,7 +196,7 @@ We can now use this skeleton to build a LibFuzzer-based fuzzer around this: Compile it, run it, and be amazed 😎 : -``` +```bash $ clang-6.0 -DUSE_LIBFUZZER -O1 -g -fsanitize=fuzzer loader.cpp -no-pie -o fuzzer -ldl $ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.4.0.0 ./fuzzer INFO: Loaded 1 modules (11 inline 8-bit counters): 11 [0x67d020, 0x67d02b), @@ -203,16 +211,16 @@ INFO: A corpus is not provided, starting from an empty corpus We're running more than 1 million executions/second/core on the function `b64decode`, not bad eh? -And in less than a 1 second, we get the heap overflow found by @mehqq_, CVE-2018-6789: +And in less than a 1 second, we get the heap overflow found by {{ twitter(user="mehqq_") }}, CVE-2018-6789: -![image_alt](/assets/images/libfuzzer-lief/fuzz-result.png) +{{ img(src="/img/libfuzzer-lief/fuzz-result.png" title="image_alt") }} > -> **Note**: Earlier this week, I was notified by mehqq_ that this is OOB read is a different bug. I will post an update soon showcasing the actual bug instead. My bad for the confusion. +> **Note**: Earlier this week, I was notified by {{ twitter(user="mehqq_") }} that this is OOB read is a different bug. I will post an update soon showcasing the actual bug instead. My bad for the confusion. > -## Final words ## +# Final words Although this technique is not as click-and-play like AFL since it requires a bit more work, it offers non-negligible pros: diff --git a/content/2018-11-02-some-time-travel-musings.md b/content/2018-11-02-some-time-travel-musings.md index 13f9b51c..6b14a6c9 100644 --- a/content/2018-11-02-some-time-travel-musings.md +++ b/content/2018-11-02-some-time-travel-musings.md @@ -1,12 +1,18 @@ -date: 2018-11-02 00:00:00 -modified: 2018-11-02 00:00:00 -title: Some Time Travel musings -author: hugsy -cover: assets/images/windbg-ttd/header.png -category: research -tags: windows,ttd,js,windbg,malware ++++ +title = "Some Time Travel musings" +authors = ["hugsy"] +date = 2018-11-02T00:00:00Z +updated = 2018-11-02T00:00:00Z -If WinDbg was already setting the standard of what modern debuggers should be like, no doubt [WinDbg Preview](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview) brings it to a next level. The JavaScript API is not perfect yet but already very efficient, so we don't have to rely on PyKd for proper (and portable) WinDbg scripting (I won't even mention WDS). As a start, I could not recommend enough reading @0vercl0k if you haven't already read it, which not only covers TTD but a lot more. +[taxonomies] +categories = ["research"] +tags = ["windows","ttd","js","windbg","malware"] + +[extra] +header_img = "/img/windbg-ttd/header.png" ++++ + +If WinDbg was already setting the standard of what modern debuggers should be like, no doubt [WinDbg Preview](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview) brings it to a next level. The JavaScript API is not perfect yet but already very efficient, so we don't have to rely on PyKd for proper (and portable) WinDbg scripting (I won't even mention WDS). As a start, I could not recommend enough reading {{ twitter(user="0vercl0k") }} if you haven't already read it, which not only covers TTD but a lot more. # Time-Travel Debugging @@ -24,11 +30,11 @@ As the name implies, Time-Travel Debugging is a tool that will allow to travel t I was curious to see what could be done so I decided to record via WinDbg a simple Notepad session. TTD is as simple as it gets: start WinDbg (as Admin), and launch the target executable after checking the `Record process with Time Travel Debugging` -![image_alt](/assets/images/windbg-ttd/startrecord.PNG) +{{ img(src="/img/windbg-ttd/startrecord.PNG" title="image_alt") }} Typed some stuff and closed notepad. WinDbg starts by reading the trace and indexing the database, and breaks at the loader entry point. The indexes (look like `XX:YY` where `X` and `Y` are hex-digits) are like coordinates that can be used to travel around so we can move to an absolute position like -```text +```txt 0:000> !tt 7213:36 Setting position: 7213:36 (12c4.1dcc): Break instruction exception - code 80000003 (first/second chance not available) @@ -46,7 +52,7 @@ That's already quite fun, but WinDbg can go a lot further. WinDbg can use LINQ to query the TTD database, to synthetize a lot more of runtime information in a very painless way. To do so, a new attribute `TTD` was added to the runtime variables `$curprocess` -```text +```txt 0:000> dx @$curprocess.TTD @$curprocess.TTD Lifetime : [2C:0, 2EB0F:0] @@ -56,7 +62,7 @@ WinDbg can use LINQ to query the TTD database, to synthetize a lot more of runti and `$cursession` -```text +```txt 0:000> dx @$cursession.TTD @$cursession.TTD : [object Object] Calls [Returns call information from the trace for the specified set of methods: TTD.Calls("module!method1", "module!method2", ...) For example: dx @$cursession.TTD.Calls("user32!SendMessageA")] @@ -68,20 +74,20 @@ and `$cursession` Utility : Methods that can be useful when analyzing time travel traces ``` -
 Note:
+{% note() %} You might want to enable DML too (by running the command `.prefer_dml 1`) if you want to click your way through those methods. -
+{% end %} Among some of the most interesting parts, we can now query function calls, like -```text +```txt 0:000> dx @$cursession.TTD.Calls("ntdll!mem*").Count() @$cursession.TTD.Calls("ntdll!mem*").Count() : 0x2ef8 ``` Will count the number of calls to function matching `ntdll!mem*` pattern, or even filter function calls per parameter -```text +```txt 0:000> dx @$cursession.TTD.Calls("Kernel*!VirtualAlloc*").Where( c => c.Parameters[3] == 0x40 ).Count() $cursession.TTD.Calls("Kernel*!VirtualAlloc*").Where( c => c.Parameters[3] == 0x40).Count() : 0x1 ``` @@ -91,7 +97,7 @@ Which will filter the calls to function matching `Kernel*!VirtualAlloc*` pattern Another useful feature is the memory access, exposed by -```text +```txt 0:000> dx $cursession.TTD [...] Memory [Returns memory access information for specified address range: TTD.Memory(startAddress, endAddress [, "rwec"])] @@ -99,7 +105,7 @@ Another useful feature is the memory access, exposed by To take the real life example of a self-decrypting packer, that would allocate some memory (likely in RWX), then decrypt the code and finally jump to it. If we were to reverse such packer, we don't care much about how the payload is decrypted (could be a simple XOR, could be AES, could be custom crypto, etc.), what we only care about is what the code looks like once decrypted. And that becomes stupidly easy with TTD + DDM: -```text +```txt // Isolate the address(es) newly allocated as RWX 0:000> dx @$cursession.TTD.Calls("Kernel*!VirtualAlloc*").Where( f => f.Parameters[3] == 0x40 ).Select( f => new {Address : f.ReturnValue } ) @@ -112,7 +118,7 @@ Done! Then you can `.writemem` that code into a file that IDA can disassemble. > > _Update (11/11/2018)_ : > -> And since all this goodness can be used from JavaScript (via the `host.namespace.Debugger` namespace), it's really not far to write scripts for automatically dump such payloads, track heap allocations, enumerate all files created etc. And it came to me a surprise (not really actually, @0vercl0k just told me), that when using the `ttd.exe` binary as a standalone, one can pass the `-children` flag allowing TTD to also record children processes. +> And since all this goodness can be used from JavaScript (via the `host.namespace.Debugger` namespace), it's really not far to write scripts for automatically dump such payloads, track heap allocations, enumerate all files created etc. And it came to me a surprise (not really actually, {{ twitter(user="0vercl0k") }} just told me), that when using the `ttd.exe` binary as a standalone, one can pass the `-children` flag allowing TTD to also record children processes. @@ -136,14 +142,14 @@ BOOL GetMessage( It is easily possible to filter those calls as mentioned earlier: -```text +```txt 0:000> dx @$cursession.TTD.Calls("user32!GetMessage*") @$cursession.TTD.Calls("user32!GetMessage*").Count() : 0x1e8 ``` One way I found to narrow so many calls down is to see is to focus rather on the message itself, which is Parameters[0] of the function call: -![image_alt](/assets/images/windbg-ttd/notepad1.png) +{{ img(src="/img/windbg-ttd/notepad1.png" title="image_alt") }} It seems that the message is always stored at 0xa30fb6fc00, and has the [following structure](https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msg) @@ -161,7 +167,7 @@ typedef struct tagMSG { We can now monitor all the memory accesses to the address 0xa30fb6fc00 -```text +```txt 0:000> dx -r1 -nv (*((wintypes!MSG *)0xa30fb6fc00)) (*((wintypes!MSG *)0xa30fb6fc00)) : {msg=0x102 wp=0x74 lp=0x140001} [Type: MSG] [+0x000] hwnd : 0x12044a [Type: HWND__ *] @@ -175,7 +181,7 @@ We can now monitor all the memory accesses to the address 0xa30fb6fc00 `MSG.wParam` in particular will hold the value of the keycode when the key is stroke, so we can also narrow it to ASCII characters -```text +```txt 0:000> dx -g @$cursession.TTD.Memory(0xa30fb6fc10, 0xa30fb6fc10+8, "w").Where(m => m.Value >= 0x20 && m.Value < 0x80) =============================================================================================================================================================================== = = (+) EventType = (+) ThreadId = (+) UniqueThreadId = (+) TimeStart = (+) TimeEnd = (+) AccessType = (+) IP = (+) Address = (+) Size = (+) Value = @@ -191,7 +197,7 @@ We can now monitor all the memory accesses to the address 0xa30fb6fc00 That's a lot more interesting so we use LINQ even further to print the characters directly by casting the Value to `char` and we get -```text +```txt 0:000> dx -g @$cursession.TTD.Memory(0xa30fb6fc10, 0xa30fb6fc10+8, "w").Where(m => m.Value >= 0x20 && m.Value < 0x80).Select( c => (char)c.Value ) ==================== = = @@ -226,9 +232,9 @@ TTD brings a new approach to traditional debugging which is a huge plus. Not onl In the mean time, I'll leave you with some links to dig deeper: -- [Debugger data model, Javascript & x64 exception handling](https://doar-e.github.io/blog/2017/12/01/debugger-data-model/){:target="_blank"} -- [Channel9 - Introduction to Time Travel Debugging](https://www.youtube.com/watch?v=qFhJLbh6zzc&){:target="_blank"} -- [Channel9 - Advanced Time Travel Debugging](https://docs.microsoft.com/en-us/shows/defrag-tools/186-time-travel-debugging-advanced){:target="_blank"} -- [WinDbg YouTube Playlist](https://www.youtube.com/playlist?list=PLjAuO31Rg973XOVdi5RXWlrC-XlPZelGn){:target="_blank"} +- [Debugger data model, Javascript & x64 exception handling](https://doar-e.github.io/blog/2017/12/01/debugger-data-model/) +- [Channel9 - Introduction to Time Travel Debugging](https://www.youtube.com/watch?v=qFhJLbh6zzc&) +- [Channel9 - Advanced Time Travel Debugging](https://docs.microsoft.com/en-us/shows/defrag-tools/186-time-travel-debugging-advanced) +- [WinDbg YouTube Playlist](https://www.youtube.com/playlist?list=PLjAuO31Rg973XOVdi5RXWlrC-XlPZelGn) Cheers! diff --git a/content/2018-12-02-quick-visualization-of-a-binary-file.md b/content/2018-12-02-quick-visualization-of-a-binary-file.md index 7322bd0c..5211dae9 100644 --- a/content/2018-12-02-quick-visualization-of-a-binary-file.md +++ b/content/2018-12-02-quick-visualization-of-a-binary-file.md @@ -1,15 +1,19 @@ -date: 2018-12-02 00:00:00 -modified: 2018-12-02 00:00:00 -title: Quick visualization of a binary file -author: hugsy -category: minis -tags: binary,visualization ++++ +title = "Quick visualization of a binary file" +authors = ["hugsy"] +date = 2018-12-02T00:00:00Z +updated = 2018-12-02T00:00:00Z +[taxonomies] +categories = ["minis"] +tags = ["binary","visualization"] ++++ -Here's a simple trick that I learned from the amazing @scanlime to quickly (and universally) visualize the distribution of byte of any binary file, using the [Portable Graymap Format (PGM)](https://en.wikipedia.org/wiki/Netpbm_format) format. + +Here's a simple trick that I learned from the amazing {{ twitter(user="scanlime") }} to quickly (and universally) visualize the distribution of byte of any binary file, using the [Portable Graymap Format (PGM)](https://en.wikipedia.org/wiki/Netpbm_format) format. On Windows: -```batch +```bat C:\> echo P5 512 4096 255 > %TEMPDIR%\visu.pgm & ^ type \path\to\file\to\visualize.whatever >> %TEMPDIR%\visu.pgm ``` @@ -22,4 +26,4 @@ $ (echo "P5 512 4096 255"; Then open the file with any image viewer like `feh` or `IrFanView`. -![evil.dll.pgm](/assets/images/quick-visualization/evil.dll.pgm.png){:width="750px"} +{{ img(src="/img/quick-visualization/evil.dll.pgm.png" title="evil.dll.pgm") }} diff --git a/content/2018-12-30-goodbye-virtualbox-hello-hyper-v.md b/content/2018-12-30-goodbye-virtualbox-hello-hyper-v.md index ae1433e3..3ba8e929 100644 --- a/content/2018-12-30-goodbye-virtualbox-hello-hyper-v.md +++ b/content/2018-12-30-goodbye-virtualbox-hello-hyper-v.md @@ -1,10 +1,16 @@ -date: 2018-12-30 00:00:00 -modified: 2018-12-30 00:00:00 -title: Goodbye VirtualBox, hello Hyper-V -author: hugsy -cover: assets/images/vbox-to-hyperv-header.png -category: research -tags: windows,,hyperv,,virtualbox,,cheatsheet ++++ +title = "Goodbye VirtualBox, hello Hyper-V" +authors = ["hugsy"] +date = 2018-12-30T00:00:00Z +updated = 2018-12-30T00:00:00Z + +[taxonomies] +categories = ["research"] +tags = ["windows","hyperv","virtualbox","cheatsheet"] + +[extra] +header_img = "/img/vbox-to-hyperv-header.png" ++++ A few scrap notes about my migration from VirtualBox to Hyper-V (in case I attempt to do the same again in the future 😁) @@ -47,7 +53,7 @@ $ sudo ./install.sh PS C:\Users\hugsy> Set-VM "Ubuntu 18.04 x64" -EnhancedSessionTransportType HvSocket ``` 4. Start the VM. When switching to the RDP session, Hyper-V Manager will prompt the desired resolution and show the XRDP prompt. - ![image_alt](https://github.com/Microsoft/linux-vm-tools/raw/master/wiki/media/xorglogin.PNG) + {{ img(src="https://github.com/Microsoft/linux-vm-tools/raw/master/wiki/media/xorglogin.PNG" title="image_alt") }} 5. Login as usual and enjoy the enhanced mode. For Fedora/RedHat, it [may also be working](https://bugzilla.redhat.com/show_bug.cgi?id=1553453). @@ -55,13 +61,13 @@ For Fedora/RedHat, it [may also be working](https://bugzilla.redhat.com/show_bug > Update (2019/04/28): > If you're using a different WM, you may also need to edit your `~/.xsession` to set proper WM value. For instance -```text +```txt env -u SESSION_MANAGER -u DBUS_SESSION_BUS_ADDRESS mate-session # for mate (could be unity, xfce4-session, gnome3, etc.) ``` -
 Note:
+{% note() %} To switch back to the regular view, simply click on **View** → uncheck **Enhanced session**. -
+{% end %} ## Sharing folders @@ -98,9 +104,9 @@ And this will conclude my pesky rant 😀 Some links to conclude: - - [https://github.com/Microsoft/linux-vm-tools](https://github.com/Microsoft/linux-vm-tools){:target="_blank"} - - [https://blogs.technet.microsoft.com/virtualization/2018/02/28/sneak-peek-taking-a-spin-with-enhanced-linux-vms/](https://blogs.technet.microsoft.com/virtualization/2018/02/28/sneak-peek-taking-a-spin-with-enhanced-linux-vms/){:target="_blank"} - - [https://nbsoftsolutions.com/blog/linux-virtualization-with-a-mounted-windows-share-on-client-hyper-v](https://nbsoftsolutions.com/blog/linux-virtualization-with-a-mounted-windows-share-on-client-hyper-v){:target="_blank"} + - [https://github.com/Microsoft/linux-vm-tools](https://github.com/Microsoft/linux-vm-tools) + - [https://blogs.technet.microsoft.com/virtualization/2018/02/28/sneak-peek-taking-a-spin-with-enhanced-linux-vms/](https://blogs.technet.microsoft.com/virtualization/2018/02/28/sneak-peek-taking-a-spin-with-enhanced-linux-vms/) + - [https://nbsoftsolutions.com/blog/linux-virtualization-with-a-mounted-windows-share-on-client-hyper-v](https://nbsoftsolutions.com/blog/linux-virtualization-with-a-mounted-windows-share-on-client-hyper-v) Cheatsheet over... diff --git a/content/2019-01-30-playing-with-windows-root-directory-object.md b/content/2019-01-30-playing-with-windows-root-directory-object.md index 366ab0b0..a34aa6fa 100644 --- a/content/2019-01-30-playing-with-windows-root-directory-object.md +++ b/content/2019-01-30-playing-with-windows-root-directory-object.md @@ -1,13 +1,18 @@ -date: 2019-01-30 00:00:00 -modified: 2019-01-30 00:00:00 -title: Scripting with Windows Root Directory Object -author: hugsy -category: research -cover: assets/images/{1910FC37-E777-418F-83EC-2A2543969515}.jpg -tags: windows,,kernel,,windbg,javascript,object-manager ++++ +title = "Scripting with Windows Root Directory Object" +authors = ["hugsy"] +date = 2019-01-30T00:00:00Z +updated = 2019-01-30T00:00:00Z +[taxonomies] +categories = [" research"] +tags = ["windows","kernel","windbg","javascript","object-manager"] -Still on my way to learning of Windows kernel, I spend considerable amount of time on [WinDbg Preview](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview). I've been [scripting my way](https://github.com/hugsy/windbg_js_scripts) to understand its components, the last in date was `nt!ObpRootDirectoryObject`. This pointer is well documented, especially @ivanlef0u's article [about it](https://www.ivanlef0u.tuxfamily.org/?p=34) (french) is a good place to start. +[extra] +header_img = "/img/{1910FC37-E777-418F-83EC-2A2543969515}.jpg" ++++ + +Still on my way to learning of Windows kernel, I spend considerable amount of time on [WinDbg Preview](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview). I've been [scripting my way](https://github.com/hugsy/windbg_js_scripts) to understand its components, the last in date was `nt!ObpRootDirectoryObject`. This pointer is well documented, especially {{ twitter(user="ivanlef0u") }}'s article [about it](https://www.ivanlef0u.tuxfamily.org/?p=34) (french) is a good place to start. ## The Status Quo @@ -30,7 +35,7 @@ NTSTATUS NtQueryDirectoryObject( _Out_opt_ PULONG ReturnLength); ``` -[source](https://github.com/hfiref0x/WinObjEx64/blob/6f6d4480d724e3430b49ff15da1b01c12793c499/Source/WinObjEx64/ntos/ntos.h#L8583-L8598){:target="_blank"} +[source](https://github.com/hfiref0x/WinObjEx64/blob/6f6d4480d724e3430b49ff15da1b01c12793c499/Source/WinObjEx64/ntos/ntos.h#L8583-L8598) Those tools are excellent, I use them big time but I was curious if it was possible to extend the data model to expose object tree in a similar fashion. Because the problem in KM (as we can see in Ivan's post) is that the structures hold a lot of pointers, `LIST_ENTRY`s and other goodies that must be dereferenced manually which turns out to be a tedious task. Also that approach prevents from easily querying the directory object. @@ -42,18 +47,18 @@ But hold your breath, here comes the Debugger Data Model... With the [help of Alex Ionescu pointing out my shortcomings](https://github.com/hugsy/windbg_js_scripts/pull/1) - but always for my benefit -, I ended up with writing [`ObjectExplorer.js`](https://github.com/hugsy/windbg_js_scripts/blob/45926ab380ba6185cc8e210d77f1a7c56ec05323/scripts/ObjectExplorer.js), a surprisingly short JS scripts for WinDbg, which parses and exposes in a structured way the content of `nt!ObpRootDirectoryObject`. -![image_alt](/assets/images/{D1BF677A-5CFD-4C16-8ABA-1492397D7E17}.jpg) +{{ img(src="/img/{D1BF677A-5CFD-4C16-8ABA-1492397D7E17}.jpg" title="image_alt") }} Not only it's all click-friendly when I'm feeling it's too complicated to type on a keyboard, but the absolute awesome thing is the total integration with LINQ, so you can actually search those objects programmatically (which is impossible with `WinObj` for instance). Say you want to enumerate the `nt!_OBJECT_TYPE` keys of all the `ObjectTypes` on your version of Windows, well... -```text +```txt lkd> dx -g -r1 @$cursession.Objects.Children.Where( obj => obj.Name == "ObjectTypes" ).First().Children.Select(o => new { Name = o.RawObjectHeader.Name, Key = (char*)&o.RawObjectHeader.Key}) ``` which produces something like: -```text +```txt ============================================================================================== = = (+) Name = (+) Key = ============================================================================================== @@ -70,14 +75,14 @@ which produces something like: Or enumerate all processes owning an ALPC port object from the `\RPC Control` directory can be seen as easily as -```text +```txt lkd> dx -r0 @$AlpcPorts = @$cursession.Objects.Children.Where( obj => obj.Name == "RPC Control" ).First().Children.Where( rpc => rpc.Type == "ALPC Port") lkd> dx -g @$AlpcPorts.Select( alpc => new { AlpcName= alpc.Name, ProcessOwnerName= (char*) alpc.Object.OwnerProcess->ImageFileName }) ``` and we get: -![image_alt](/assets/images/{68EB5886-B508-4F69-81E2-DDC726638542}.png) +{{ img(src="/img/{68EB5886-B508-4F69-81E2-DDC726638542}.png" title="image_alt") }} You get the gist. Pretty cool, right? diff --git a/content/2019-03-17-small-dumps-in-the-big-pool.md b/content/2019-03-17-small-dumps-in-the-big-pool.md index f6b4a69b..acfdfab2 100644 --- a/content/2019-03-17-small-dumps-in-the-big-pool.md +++ b/content/2019-03-17-small-dumps-in-the-big-pool.md @@ -1,35 +1,40 @@ -date: 2019-03-17 00:00:00 -modified: 2019-03-17 00:00:00 -title: Small dumps in the big pool -author: hugsy -category: research -cover: assets/images/f4300721f56d68c92db76aa03c3bbd54.png -og_image: assets/images/f4300721f56d68c92db76aa03c3bbd54.png -tags: windows,kernel,pool,rw-primitive ++++ +title = "Small dumps in the big pool" +authors = ["hugsy"] +date = 2019-03-17T00:00:00Z +updated = 2019-03-17T00:00:00Z + +[taxonomies] +categories = ["research"] +tags = ["windows","kernel","pool","rw-primitive"] + +[extra] +header_img = "/img/f4300721f56d68c92db76aa03c3bbd54.png" ++++ Or, on how to use the (Windows 10) new field `_ETHREAD.ThreadName` to stabilize kernel RW primitives ## SetThreadDescription() as a way to allocate controlled kernel pools -Keeping on with experimenting with Windows 10 I noticed a field part of the `nt!_ETHREAD` structure, called `ThreadName`. For a minute, the field name misled me to think threads were now [Named Objects](https://docs.microsoft.com/en-us/windows/desktop/sync/object-names) on Windows. What it is instead, is a convenient and native way to name a thread, any thread by attaching a `UNICODE_STRING` structure to it. Thanks to @PetrBenes's invaluable [`ntdiff`](https://ntdiff.github.io/) it became clear that this field was introduced with Windows 10, more specifically 1607. +Keeping on with experimenting with Windows 10 I noticed a field part of the `nt!_ETHREAD` structure, called `ThreadName`. For a minute, the field name misled me to think threads were now [Named Objects](https://docs.microsoft.com/en-us/windows/desktop/sync/object-names) on Windows. What it is instead, is a convenient and native way to name a thread, any thread by attaching a `UNICODE_STRING` structure to it. Thanks to {{ twitter(user="PetrBenes") }}'s invaluable [`ntdiff`](https://ntdiff.github.io/) it became clear that this field was introduced with Windows 10, more specifically 1607. -![ntdiff](/assets/images/small-pool/ntdiff.png) +{{ img(src="/img/small-pool/ntdiff.png" title="ntdiff") }} [Source](https://ntdiff.github.io/#versionLeft=Win8.1_U1%2Fx64%2FSystem32&filenameLeft=ntoskrnl.exe&typeLeft=Standalone%2F_ETHREAD&versionRight=Win10_1607_RS1%2Fx64%2FSystem32&filenameRight=ntoskrnl.exe&typeRight=Standalone%2F_ETHREAD) So how to use it? Is it even reachable? The answer was as immediate as [Googling "windows set thread name"](https://google.com/search?q=windows+10+set+thread+name) which leads to an [MSDN article](https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2017). This article mentions the [`SetThreadDescription()`](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreaddescription) in `processthreadsapi.h`. Disassembling `kernelbase.dll` shows that this function is merely a wrapper around the syscall `NtSetInformationThread()` with a `ThreadInformationClass` set to 0x26 (`ThreadNameInformation`). -![ida-setthreaddescription](/assets/images/small-pool/ida-setthreaddescription.png) +{{ img(src="/img/small-pool/ida-setthreaddescription.png" title="ida-setthreaddescription") }} Once in `ntoskrnl` (IDA), the syscall performs various checks (is the `_ETHREAD.ThreadName` already allocated, is the input size and buffer correct etc.), and then call `ExAllocatePoolWithTag()` with a tag of `ThNm` and as `NonPagedPoolNx`, and the size provided by the `UNICODE_STRING` structure, plus `sizeof(UNICODE_STRING)`. Finally, the user buffer will be `memmove`-ed into this new pool. -![ntsetinformationthread-1](/assets/images/small-pool/ntsetinformationthread-1.png) +{{ img(src="/img/small-pool/ntsetinformationthread-1.png" title="ntsetinformationthread-1") }} Since the unicode buffer and its size are fully user controlled, this means that the syscall `NtSetInformationThread(0x26)` provides a way to allocate an arbitrary sized pool in the kernel, for each thread we create and/or can open a handle to via `OpenThread()`. -
 Note:
+{% note() %} The code was tested on Windows 10 RS5 x64. To work on 32b one might need to adjust the offsets. Also Windows must be at least 1607. -
+{% end %} The following code is enough to populate the `_ETHREAD.ThreadName` of a designed thread: @@ -37,12 +42,12 @@ The following code is enough to populate the `_ETHREAD.ThreadName` of a designed The acute observer may notice that only `THREAD_SET_LIMITED_INFORMATION` class information is used. Therefore setting thread name with `ThreadNameInformation` is an operation that is not considered privileged and should work very reliably, just like `THREAD_QUERY_LIMITED_INFORMATION` to retrieve the thread name. -![image_alt](/assets/images/small-pool/setthreadname-1.png) +{{ img(src="/img/small-pool/setthreadname-1.png" title="image_alt") }} From WinDbg, the `!poolfind` command can be used to filter by tag name, in this case `ThNm` (0x6d4e6854), or query `!pool` with the address from the field `_ETHREAD!ThreadName`. This confirms that we fully control the content and size of pools. To be in the large pool, the chunk must be of at least 0x1000 bytes, making the minimum actual pool data size of 0x1000-0x10 bytes (for the header). And for the maxiumum allocatable size, during this experiment it was shown possible to allocate thread name up to 0xfff0 bytes (65520): -```text +```txt C:\Users\IEUser\Desktop>pslist -nobanner -d notepad Thread detail for MSEDGEWIN10: @@ -55,7 +60,7 @@ tid=5488 data stored at FFFFDD07B6F8C010 ``` -![image_alt](/assets/images/small-pool/setthreadname-2.png) +{{ img(src="/img/small-pool/setthreadname-2.png" title="image_alt") }} Which makes sense, since larger size would overflow the `Length` field of the `UNICODE_STRING` (i.e. `sizeof(WORD)`), which is checked during the `NtSetInformationThread(ThreadNameInformation)` syscall. @@ -86,7 +91,7 @@ HRESULT hRes = NtQuerySystemInformation( ``` If large enough the buffer `pBuffer` will be populated by the kernel by `N` entries of `SYSTEM_BIGPOOL_ENTRY` structured as follow: -``` +```txt 0x00 NumberOfEntries Entry0 @@ -100,6 +105,7 @@ Entry1 { 0x20 Entry1.Address [...] +} ``` Which becomes trivial to parse to get the thread kernel address, simply by looking up for the entry that would match the condition `strncmp( info->PoolTag, "ThNm", 4)==0`. In the case of multi-threaded process with many ThreadName entries, it is possible to refine the search by using the size as a secondary search index: @@ -136,7 +142,7 @@ ULONG_PTR LookForThreadNamePoolAddress(PVOID pSystemBigPoolInfoBuffer, DWORD64 d That's pretty much it. [Put it all together](https://gist.github.com/hugsy/d89c6ee771a4decfdf4f088998d60d19) and you get: -``` +```bat z:\> AllocateLargePool.exe 26948 4096 [*] Target TID=26948 [+] Data from buffer 000001BCD71A0000 (16 bytes) written at FFFFD8001E966010 @@ -148,7 +154,7 @@ What about local DoS? Well yes, it is a pretty simple to destabilize the system As a side note, on my test VM (Windows 10 RS5 with 2 vCpus and 2GB of RAM), I could force a process to spawn ~0xb900 threads before the system became unusable. -![image_alt](/assets/images/small-pool/dos-1.png) +{{ img(src="/img/small-pool/dos-1.png" title="image_alt") }} ## Final words diff --git a/content/2020-03-09-unexpected-logic-bug-win32k.md b/content/2020-03-09-unexpected-logic-bug-win32k.md index bbd15bab..04f3a9d8 100644 --- a/content/2020-03-09-unexpected-logic-bug-win32k.md +++ b/content/2020-03-09-unexpected-logic-bug-win32k.md @@ -1,10 +1,16 @@ -date: 2020-03-09 00:00:00 -modified: 2020-03-09 00:00:00 -title: An unexpected logic bug on Win32k -author: hugsy -category: research -tags: windows,kernel,logic,bug,win32k -cover: assets/images/CDA9D98DF912DE08CB61AD0A3A148723A37BC3F3.png ++++ +title = "An unexpected logic bug on Win32k" +authors = ["hugsy"] +date = 2020-03-09T00:00:00Z +updated = 2020-03-09T00:00:00Z + +[taxonomies] +categories = ["research"] +tags = ["windows","kernel","logic","bug,win32k"] + +[extra] +header_img = "/img/CDA9D98DF912DE08CB61AD0A3A148723A37BC3F3.png" ++++ ## The short version @@ -20,20 +26,20 @@ int WinMain(HINSTANCE h, HINSTANCE ins, LPSTR cmd, int nb) Just compile, run (here on a build 19569.1000 x64) and enjoy: -![bsod](https://i.imgur.com/DRxULeh.png) +{{ img(src="https://i.imgur.com/DRxULeh.png" title="bsod") }} ## The less short version -Reversing `Win32k.sys` driver has been my hobby lately mostly to understand it (finally) seriously - if there is such a thing. This is a really small funny logic bug I encountered while reversing it, which I don't feel too bad disclosing since there is [no security exploitability](https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria){:target="_blank"} (simply annoying your sysadmin). +Reversing `Win32k.sys` driver has been my hobby lately mostly to understand it (finally) seriously - if there is such a thing. This is a really small funny logic bug I encountered while reversing it, which I don't feel too bad disclosing since there is [no security exploitability](https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria) (simply annoying your sysadmin). ### The juicy part -The legacy function [EndTask](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-endtask){:target="_blank"} can be used to forcefully close the specific window whose handle is passed as argument, and free all associated resources. Although deprecated according to the MSDN, it is still callable even on the latest Windows versions. +The legacy function [EndTask](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-endtask) can be used to forcefully close the specific window whose handle is passed as argument, and free all associated resources. Although deprecated according to the MSDN, it is still callable even on the latest Windows versions. -The function `user32!EndTask()` is merely a wrapper designed to forward some specific messages to the [CSRSS](https://en.wikipedia.org/wiki/Client/Server_Runtime_Subsystem){:target="_blank"} via an ALPC, using the exported function -`ntdll!CsrClientCallServer` with the ApiNumber 0x30401. Easily enough, the function takes the handle to the window to shut down. The function operates with the thread's token, and is unprivileged. Starting playing around, I remembered that `GetDesktopWindow()` will return a valid handle to the desktop window, but has [many interesting properties](https://devblogs.microsoft.com/oldnewthing/20040224-00/?p=40493){:target="_blank"} including that that it is owned by `csrss.exe`. That can be quickly demonstrated using the following code: +The function `user32!EndTask()` is merely a wrapper designed to forward some specific messages to the [CSRSS](https://en.wikipedia.org/wiki/Client/Server_Runtime_Subsystem) via an ALPC, using the exported function +`ntdll!CsrClientCallServer` with the ApiNumber 0x30401. Easily enough, the function takes the handle to the window to shut down. The function operates with the thread's token, and is unprivileged. Starting playing around, I remembered that `GetDesktopWindow()` will return a valid handle to the desktop window, but has [many interesting properties](https://devblogs.microsoft.com/oldnewthing/20040224-00/?p=40493) including that that it is owned by `csrss.exe`. That can be quickly demonstrated using the following code: ```c int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) @@ -49,10 +55,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine ``` which will output the PID of CSRSS -![finding_csrss](https://i.imgur.com/Q4XsJZP.png) +{{ img(src="https://i.imgur.com/Q4XsJZP.png" title="finding_csrss") }} -In turn, CSRSS will consume that message and call `winsrvext!SrvEndTask()` then `winsrvext!EndTask()`. In this function, in order to determine the process to terminate `csrss` will invoke `GetWindowThreadProcessId()` and will use the found process id value to look into the [`CSR_PROCESS`](http://www.geoffchappell.com/studies/windows/win32/csrsrv/api/process/process.htm){:target="_blank"} linked +In turn, CSRSS will consume that message and call `winsrvext!SrvEndTask()` then `winsrvext!EndTask()`. In this function, in order to determine the process to terminate `csrss` will invoke `GetWindowThreadProcessId()` and will use the found process id value to look into the [`CSR_PROCESS`](http://www.geoffchappell.com/studies/windows/win32/csrsrv/api/process/process.htm) linked list (via `csrsrv!CsrRootProcess`), and find the `CSR_PROCESS` structure associated to such PID. From `winsrvext.dll`: ```asm @@ -70,7 +76,7 @@ _EndTask+AF call cs:__imp_GetWindowThreadProcessId And therein lied the bug: as shown above with the small C snippet, the owner of `GetDesktopWindow()` is `csrss` itself, therefore the lookup will return the `CSR_PROCESS` structure of `CSRSS` (which happens to be the first entry in the `CsrRootProcess` linked list). Finally, `winsrvext!EndTask()` will proceed to call `ntdll!NtTerminateProcess()` passing the handle to the process `CSRSS`, which has the value `(HANDLE)-1` (i.e. `GetCurrentProcess()`). WinDbg can be used to confirm that behavior: -``` +```txt 0: kd> dps poi( csrsrv!CsrRootProcess ) 00000218`7d004550 00000000`00000c14 <- CSR_PROCESS.ClientId 00000218`7d004558 00000000`00000c18 @@ -89,7 +95,7 @@ process `CSRSS`, which has the value `(HANDLE)-1` (i.e. `GetCurrentProcess()`). Therefore, this will make `CSRSS` killing itself when invoking calling the syscall `nt!NtTerminateProcess(GetCurrentProcess(), 0 )`. As a critical process, killing CSRSS will immediately result in a BSoD, which BugCheck clearly shows. Also note that this crash can be triggered by any user even with any privilege. In WinDbg the faulting stack trace of our BSoD retraces exactly everything we show: -``` +```txt CRITICAL_PROCESS_DIED (ef) A critical system process died [...] diff --git a/content/2020-05-23-enumerating-process-from-kd.md b/content/2020-05-23-enumerating-process-from-kd.md index 200deb0f..3c71d053 100644 --- a/content/2020-05-23-enumerating-process-from-kd.md +++ b/content/2020-05-23-enumerating-process-from-kd.md @@ -1,9 +1,13 @@ -date: 2020-05-23 00:00:00 -modified: 2020-05-23 00:00:00 -title: Enumerating processes from KD -author: hugsy -category: minis -tags: windows, kernel, process ++++ +title = "Enumerating processes from KD" +authors = ["hugsy"] +date = 2020-05-23T00:00:00Z +updated = 2020-05-23T00:00:00Z + +[taxonomies] +categories = ["minis"] +tags = ["windows", "kernel", "process"] ++++ This is tiny Post-It post to remind of different ways to enumerate processes from KD: diff --git a/content/2020-06-15-playing_with_self_reference_pml4_entry.md b/content/2020-06-15-playing_with_self_reference_pml4_entry.md index c7a47e15..eb6f3814 100644 --- a/content/2020-06-15-playing_with_self_reference_pml4_entry.md +++ b/content/2020-06-15-playing_with_self_reference_pml4_entry.md @@ -1,26 +1,32 @@ -date: 2020-06-15 00:00:00 -modified: 2020-06-15 00:00:00 -title: Some toying with the Self-Reference PML4 Entry -author: hugsy -category: research -tags: windows, kernel, mmu, x64 -cover: assets/images/f7803990-4baa-4a9a-a09b-0cde30694fa6.png - -Sometimes you read about a [completely awesome exploitation technique](#1), so you want to go deeper. So this is my notes about how trying to totally understand the exploitation of [CVE-2020-0796](#2), I ended up struggling finding good explanation about a critical structure of Windows paging mechanism: the "Self-Reference PML4 Entry". ++++ +title = "Some toying with the Self-Reference PML4 Entry" +authors = ["hugsy"] +date = 2020-06-15T00:00:00Z +updated = 2024-07-01T00:00:00Z + +[taxonomies] +categories = ["research"] +tags = ["windows","kernel", "mmu", "x64"] + +[extra] +header_img = "/img/f7803990-4baa-4a9a-a09b-0cde30694fa6.png" ++++ + +Sometimes you read about an awesome exploitation technique ([#1](#links)), so you want to go deeper. So this is my notes about how trying to totally understand the exploitation of CVE-2020-0796 ([#2](#links)), I ended up struggling finding good explanation about a critical structure of Windows paging mechanism: the "Self-Reference PML4 Entry". _Disclaimer_: If you came here for new stuff, so let me put your mind at peace: There's nothing new here, I don't claim to find anything what's being found and said by people way smarter, and I have probably understood it wrong anyway so don't judge/quote me. Also the post will only talk be about x64 and Windows here (and having a (L)KD open can help to follow along). ## MMU 101 -Although this post won't be only about the MMU (there's [a book for that](#3)), some background is required for understanding why there is a need for the so-called Self-Reference PML4 entry. The root question for that is a simple (but not trivial) one: how does the processor read/write a block of physical memory, **only** by knowing the virtual address, or in layman's term, how to go from Virtual Address to Physical Address? +Although this post won't be only about the {{ abbr(abbr="MDL", title="Memory Management Unit") }} (there's a book for that [#3](#links)), some background is required for understanding why there is a need for the so-called Self-Reference PML4 entry. The root question for that is a simple (but not trivial) one: how does the processor read/write a block of physical memory, **only** by knowing the virtual address, or in layman's term, how to go from Virtual Address to Physical Address? ### Segmentation -On Intel and AMD processors, a virtual address is a combination of a _segment number_ **and** _a linear address_, or `segment_number:linear_address` and even on 64b architecture segmentation is still necessary. So in long mode, a code virtual address is never just `0xLinearAddress` but always `cs:0xLinearAddress`, data is `ds:0xLinearAddress`, stack is `ss:0xLinearAddress`, and so on, where `cs`, `ds`, `ss` register holds a WORD value corresponding to an index (with the 2 least significant bit OR-ed, designating the CPL) . The segment number will be added to the value of the register `gdtr` and will get the segment descriptor: +On Intel and AMD processors, a virtual address is a combination of a _segment number_ **and** _a linear address_, or `segment_number:linear_address` and even on 64b architecture segmentation is still necessary. So in long mode, a code virtual address is never just `0xLinearAddress` but always `cs:0xLinearAddress`, data is `ds:0xLinearAddress`, stack is `ss:0xLinearAddress`, and so on, where `cs`, `ds`, `ss` register holds a WORD value corresponding to an index (with the 2 least significant bit OR-ed, designating the {{ abbr(abbr="CPL", title="Current Privilege Level") }}) . The segment number will be added to the value of the register `gdtr` and will get the segment descriptor: -```text +```txt kd> r cs, rip, gdtr cs=0010 rip=fffff80041e811e0, gdtr=fffff80044b5dfb0 kd> dd @gdtr + @cs l2 @@ -32,10 +38,10 @@ Binary: [..] 00000000 00100000 10011011 00000000 Which we can parse combined with the format given by the AMD manual: -![image_alt](https://i.ibb.co/NNgJdgz/image.png) +{{ img(src="https://i.ibb.co/NNgJdgz/image.png" title="image_alt") }} (Src: AMD Programmer's Manual Volume 2) -```text +```txt 0x00209b00 = 0000 0000 ‭ 0010 0000 1001 1011 0000 0000‬ [BaseL ] gdLa P| 1 1CRA [BaseM ] | @@ -45,15 +51,15 @@ Which we can parse combined with the format given by the AMD manual: [ BaseAddress 15:0 ] [ Seg Limit 15:0 ] ``` -The current CPL being given by the 2 lowest bytes of CS, it is now easy to understand how the CPU performs privilege check: by simply comparing the CPL from CS register and DPL from the segment descriptor, or if you prefer a visual diagram from the AMD manual: +The {{ abbr(abbr="CPL", title="Current Privilege Level") }} being given by the 2 lowest bytes of CS, it is now easy to understand how the CPU performs privilege check: by simply comparing the {{ abbr(abbr="CPL", title="Current Privilege Level") }} from CS register and {{ abbr(abbr="DPL", title="Descriptor Privilege Level") }} from the segment descriptor, or if you prefer a visual diagram from the AMD manual: -![image_alt](https://i.ibb.co/kDFzxB8/image.png) +{{ img(src="https://i.ibb.co/kDFzxB8/image.png" title="image_alt") }} (Src: AMD Programmer's Manual Volume 2) As we saw earlier, the `Address` and `Limit` parts of the descriptor are equal to 0 in Long-Mode (64-bit) - this may be the source of confusion I read in some blog posts (but no name shaming, it's not the point 😋). Also if you're lazy (like me) and addicted to WinDbg (like me), the `dg` command will pretty-print all those info for you: -```text +```txt kd> dg @cs P Si Gr Pr Lo Sel Base Limit Type l ze an es ng Flags @@ -70,11 +76,11 @@ There is plenty more to say about the segmentation mechanism on x86, but for our ### Paging -Preparing this post, I came across [this blog post](https://connormcgarr.github.io/paging/){:target="_blank"} that [@33y0re](https://twitter.com/33y0re){:target="_blank"} wrote recently, and where he did a really good job summarizing how paging works on x86-64 long-mode, and how to explore it on Windows. Therefore I will send you reader to his article, and assume from then on you know of PML4, PDPT, PD, PT and what a canonical linear address is. +Preparing this post, I came across [this blog post](https://connormcgarr.github.io/paging/) that {{ twitter(user="33y0re") }} wrote recently, and where he did a really good job summarizing how paging works on x86-64 long-mode, and how to explore it on Windows. Therefore I will send you reader to his article, and assume from then on you know of PML4, PDPT, PD, PT and what a canonical linear address is. The best summary can be given by this diagram (again from AMD's manual) -![image_alt](https://i.ibb.co/k5TDWgw/image.png) +{{ img(src="https://i.ibb.co/k5TDWgw/image.png" title="image_alt") }} _Source: AMD Programmer's Manual Volume 2_ @@ -83,7 +89,7 @@ _Source: AMD Programmer's Manual Volume 2_ Back to the problem at hand, i.e. understand how does the CPU go from VA to PA, there is an intrinsic problem: the CPU only uses virtual address so how could the processor manipulates the permissions, flags, etc. of those PTEs which are physical? Simply by mapping the PTE tables in VAS, right? But that creates a recursive problem, because we still don't know how to go from VA to PA. And that's precisely where "Self-Reference PML4 entry" comes in. But let's go back a bit. -When a new process is created, a new PML4 is also allocated holding the physical root address for our process address space. From that physical root address and with all the offsets from the VA itself, the MMU can crawl down the physical page directories until getting the wanted data (see "Paging" above). This physical address is stored in the [`nt!_KPROCESS`](https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/2004%2020H1%20(May%202020%20Update)/_KPROCESS){:target="_blank"} structure of the process, precisely in `_KPROCESS.DirectoryTableBase`. +When a new process is created, a new PML4 is also allocated holding the physical root address for our process address space. From that physical root address and with all the offsets from the VA itself, the {{ abbr(abbr="MDL", title="Memory Management Unit") }} can crawl down the physical page directories until getting the wanted data (see "Paging" above). This physical address is stored in the [`nt!_KPROCESS`](https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/2004%2020H1%20(May%202020%20Update)/_KPROCESS) structure of the process, precisely in `_KPROCESS.DirectoryTableBase`. To experiment this behavior, we can create a simple program that will only `int3` so that KD gets the hand while still in user-mode: @@ -93,7 +99,7 @@ void main() {__asm__("int3;"); } Compile and execute, and as expected KD notifies the breakpoint: -```text +```txt Break instruction exception - code 80000003 (first chance) int3+0x6d08: 0033:00007ff7`83f26d08 cc int 3 @@ -107,9 +113,9 @@ So when a process switch occurs, the kernel can move `nt!_EPROCESS.KernelObject. But we slightly digressed, back to the topic: in order to map in the VAS our PML4 which is in physical address space, the kernel needs a way to always know at least one entry of the PML4: this is the "Self-Reference Entry". Also seen to be called "auto-entry", the *Self-Reference Entry* (or "self-ref entry" for short) is a special PML4 index (so then only 9-bit in size) that only the kernel knows (hence between 0x100-0x1ff), and whose content points the physical address of the PML4 itself. By doing so, Windows kernel gives itself an easy way to reach by a virtual address, any directory (PML4, PDPT, PDE, etc.). -On Windows 7, the self-ref entry index is a static value (0x1ed) whereas Windows 10 randomizes it on boot. So to understand why this Self-Reference Entry is helpful, let's process a virtual address like the MMU would: the PML4 index corresponds to the 39:47 bits of a VA, so the value 0x1ed (or 0b111101101) would be as follow: +On Windows 7, the self-ref entry index is a static value (0x1ed) whereas Windows 10 randomizes it on boot. So to understand why this Self-Reference Entry is helpful, let's process a virtual address like the {{ abbr(abbr="MDL", title="Memory Management Unit") }} would: the PML4 index corresponds to the 39:47 bits of a VA, so the value 0x1ed (or 0b111101101) would be as follow: -```text +```txt Bi| 6 ... 4444 4444 3333 ... t#| 3 ... 7654 3210 9876 ... Va| 1111 0110 1xxx <<-- 0x1ed @@ -120,7 +126,7 @@ So for all Windows from 7 to 10 TH2, the PML4 table of **all processes** was alw So let's translate a special VA 0xFFFFF6FB\`7DBED000‬ to a physical address (PA): by decomposing its indexes we get: -```text +```txt * pml4e_offset : 0x1ed * pdpe_offset : 0x1ed * pde_offset : 0x1ed @@ -128,11 +134,13 @@ So let's translate a special VA 0xFFFFF6FB\`7DBED000‬ to a physical address (P * offset : 0x000 ``` -
 Note: the output is from my [`PageExplorer.js`](https://github.com/hugsy/windbg_js_scripts/blob/master/scripts/PageExplorer.js){:target="_blank"} WinDbg script.
+{% note() %} +the output is from my [`PageExplorer.js`](https://github.com/hugsy/windbg_js_scripts/blob/master/scripts/PageExplorer.js) WinDbg script. +{% end %} The PML4E of the current process can be reached at `CR3 + 0x1ed*@$ptrsize`: but the content is the base physical address of the PML4 itself again! So the PDPE will itself also translate to the PML4 and so on until we read the `PTE+offset` which again will return the base address of the PML4 (because `offset=0`)! So what we get is an easy way to read the content of not just the PML4 itself, but any page directory, and all simply by knowing that 9-bit value (and therefore, calculating the corresponding PXE)! So you can artificially create VA simply by their offset, for instance to read the PageTable instead? -```text +```txt * pml4e_offset : 0x1ed * pdpe_offset : 0x000 * pde_offset : 0x000 @@ -141,7 +149,7 @@ The PML4E of the current process can be reached at `CR3 + 0x1ed*@$ptrsize`: but ``` And build the address as -```text +```txt 0xffff<<48 | $pml4e_offset<<39 | $pdpe_offset<<30 | $pde_offset<<21 | $pte_offset<<12 | $offset => 0xffff<<48 | 0x1ed<<39 | 0<<30 | 0<<21 | 0<<12 | 0 ``` @@ -155,10 +163,10 @@ To summarize (or if you just jumped to the end of this section), what's awesome ## What about Windows 10 RS1+? -Up until Windows 10 TH2, the magic index for the Self-Reference PML4 entry was 0x1ed as mentioned above. But what about Windows 10 from 1607? Well Microsoft uped their game, as a [constant battle for improving Windows security](https://www.blackhat.com/docs/us-16/materials/us-16-Weston-Windows-10-Mitigation-Improvements.pdf){:target="blank"} the index is randomized at boot-time, so 0x1ed is now one of the 512 possible values (i.e. 9-bit index) that the Self-Reference entry index can have. And side effect, it also broke some of their own tools, like the `!pte2va` WinDbg command. +Up until Windows 10 TH2, the magic index for the Self-Reference PML4 entry was 0x1ed as mentioned above. But what about Windows 10 from 1607? Well Microsoft uped their game, as a [constant battle for improving Windows security](https://www.blackhat.com/docs/us-16/materials/us-16-Weston-Windows-10-Mitigation-Improvements.pdf) the index is randomized at boot-time, so 0x1ed is now one of the 512 possible values (i.e. 9-bit index) that the Self-Reference entry index can have. And side effect, it also broke some of their own tools, like the `!pte2va` WinDbg command. On Windows 2004 x64, 0xFFFFF680`00000000 points to nothing (at least most of the times 🤓) -```text +```txt kd> db 0xFFFFF680`00000000 l20 fffff680`00000000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? fffff680`00000010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? @@ -166,7 +174,7 @@ fffff680`00000010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????? But is it really 512 values for the entry? Well no, because the most significant bit must be set to 1 for the Sign-Extension to properly make it a kernel canonical address. So it is more 256 values (from 0x100 to 0x1ff). If we're in KD, this index can be retrieved by a new global symbol `nt!MmPteBase`, and so the self-reference entry can be known as: -```text +```txt kd> dq nt!MmPteBase l1 fffff804`29e29388 fffff880`00000000 kd> ? (poi(nt!MmPteBase) >> 0n39) & 0x1ff @@ -176,13 +184,13 @@ Evaluate expression: 497 = 00000000`000001f1 In our current KD session on a Windows 2004 (on Hyper-V), the self-reference entry has the index of 0x1f1. So now we have the PML4 index, we can craft the virtual address to get its physical address: - calculate the PTE VA -```text +```txt kd> ? 0xffff<<0n48 | 0x1f1<<0n39 | 0x1f1<<0n30 | 0x1f1<<0n21 | 0x1f1<<0n12 | 000 Evaluate expression: -7711643201536 = fffff8fc`7e3f1000 ``` - get the entry info -```text +```txt kd> !pte 0xfffff8fc7e3f1000 @$pte(0xfffff8fc7e3f1000) : VA=0xfffff8fc7e3f1000, PA=0x4c7d1000, Offset=0x0 va : -7711643201536 @@ -203,7 +211,7 @@ kd> !pte 0xfffff8fc7e3f1000 As we see, for each entry (PML4E, PDPTE, etc.) the base address found is always the same **and** matches the content of `CR3`. We can also easily prove this is the self-reference entry index: as stated above, the entry index (in our example 0x1f1) has to be the same for all processes, meaning that if we break into another process context, the kernel PXE will be the same. Let's try with our `int3.exe` again: -```text +```txt Break instruction exception - code 80000003 (first chance) 0033:00007ff6`2ac36d08 cc int 3 kd> !pte 0xfffff8fc7e3f1000 @@ -224,7 +232,7 @@ kd> !pte 0xfffff8fc7e3f1000 ``` And to confirm the VA points to the correct PA: -```text +```txt kd> db 0xfffff8fc7e3f1000 fffff8fc`7e3f1000 67 28 16 62 00 00 00 8a-67 58 c8 11 00 00 00 8a g(.b....gX...... fffff8fc`7e3f1010 00 00 00 00 00 00 00 00-67 f8 40 77 00 00 00 8a ........g.@w.... @@ -238,7 +246,7 @@ Same data, the VA to PA conversion was successful, and the recursive page entrie Last, one can ask: is there any kind of randomization of the allocation of the physical pages themselves? Legit question, and I experimented using some LINQ querying: -```text +```txt kd> dx -g @$cursession.Processes.Select( p => new { ProcessName = p.Name, Pml4Base = p.KernelObject.Pcb.DirectoryTableBase & 0xfffffffffff000}) ``` @@ -259,7 +267,7 @@ Across several reboots in my VM labs, only 2 matches are shown consistently | 0x4 | System | 0x6d4000 | -0x1aa000 for the physical address of a Gen1 (BIOS) Hyper-V VM, and 0x6d4000 for a Gen2 (UEFI). This seems to partially coincide with what was said in [Ricerca's article](#1) about the fact that the PML4 for System is at unrandomized physical address in most cases. From my limited testing the following physical addresses were found consistently (for Windows 2004 x64 with Kd): +0x1aa000 for the physical address of a Gen1 (BIOS) Hyper-V VM, and 0x6d4000 for a Gen2 (UEFI). This seems to partially coincide with what was said in Ricerca's article (see [#1](#links)) about the fact that the PML4 for System is at unrandomized physical address in most cases. From my limited testing the following physical addresses were found consistently (for Windows 2004 x64 with Kd): | Platform | PML4 Base | @@ -272,14 +280,16 @@ Across several reboots in my VM labs, only 2 matches are shown consistently -
 Note: if you have other values on your environment (Qemu, VMware), feel free to contact me and I'll update the table with the result of the KD command
+{% note() %} +if you have other values on your environment (Qemu, VMware), feel free to contact me and I'll update the table with the result of the KD command +{% end %} -```text +```txt dx @$cursession.Processes.Where( p => p.Name == "System").First().KernelObject.Pcb.DirectoryTableBase & ~0xfff ``` -And this is really the subtlety of Ricerca's exploit: they showed that only with a fixed physical address (associated to the SYSTEM process), and a fixed virtual area (the `nt!_KUSER_SHARED_DATA` section at 0xfffff780\`00000000) that is always at a known location since NT4, one can create an MDL used in Direct Memory Access, and achieve arbitrary read to virtual addresses simply by recursing through the PML4E, the PDPTE, etc. just like the MMU does. Since they could read the PML4 entirely at a fixed physical address, say 0x1aa000, they could determine the index of the "Self-Reference Entry" from a simple for-loop going through the PML4 page (very approximate pseudo-code): +And this is really the subtlety of Ricerca's exploit: they showed that only with a fixed physical address (associated to the SYSTEM process), and a fixed virtual area (the `nt!_KUSER_SHARED_DATA` section at 0xfffff780\`00000000) that is always at a known location since NT4, one can create an {{ abbr(abbr="MDL", title="Memory Descriptor List") }} used in Direct Memory Access, and achieve arbitrary read to virtual addresses simply by recursing through the PML4E, the PDPTE, etc. just like the {{ abbr(abbr="MDL", title="Memory Management Unit") }} does. Since they could read the PML4 entirely at a fixed physical address, say 0x1aa000, they could determine the index of the "Self-Reference Entry" from a simple for-loop going through the PML4 page (very approximate pseudo-code): ```python system_pml4_root = 0x1aa000 @@ -295,13 +305,13 @@ for index in range(system_pml4_root, system_pml4_root+size_of_page, size_of_entr print("self-reference entry is at index: %d" % index) ``` -I hope not to make it sound simple, it is not and took me quite some time to figure out, so massive props to [`@hugeh0ge`](https://twitter.com/hugeh0ge){:target="_blank"} and [`@_N4NU_`](https://twitter.com/_N4NU_){:target="_blank"} for the technique, and [`@chompie1337`](https://web.archive.org/web/20220619035731/twitter.com/chompie1337){:target="_blank"} for the implementation. This technique provides a somewhat reliable way to defeat KASLR, SMEP & SMAP with no other vulnerability, but by mere knowledge of Intel processors and Windows memory management inner workings, for the vulnerability CVE-2020-0796, which, due to Microsoft's effort, made it tough. +I hope not to make it sound simple, it is not and took me quite some time to figure out, so massive props to {{ twitter(user="hugeh0ge`](https://twitter.com/hugeh0ge) and [`@_N4NU_") }} for the implementation. This technique provides a somewhat reliable way to defeat KASLR, SMEP & SMAP with no other vulnerability, but by mere knowledge of Intel processors and Windows memory management inner workings, for the vulnerability CVE-2020-0796, which, due to Microsoft's effort, made it tough. Thanks for reading...✌ _Update_: A `@$selfref()` function was added to `PageExplorer.js`, allowing to easily retrieve the PML4 self-reference (tested 8 -> 11) -```text +```txt 0: kd> dx @$selfref() @$selfref() : 0x1ec 0: kd> dx @$ptview().pml4_table[ @$selfref() ].PhysicalPageAddress == @$ptview().pml4_table[ @$selfref() ].Children[ @$selfref() ].PhysicalPageAddress @@ -324,8 +334,8 @@ _Update_: A `@$selfref()` function was added to `PageExplorer.js`, allowing to e What started picking my curiosity: - - [1] [Ricerca Security on exploiting the same bug](https://ricercasecurity.blogspot.com/2020/04/ill-ask-your-body-smbghost-pre-auth-rce.html){:name="1"} - - [2] [Chompie1337's CVE-2020-0796 exploit](https://github.com/chompie1337/SMBGhost_RCE_PoC/blob/master/exploit.py){:name="2"} + - [1] [Ricerca Security on exploiting the same bug](https://ricercasecurity.blogspot.com/2020/04/ill-ask-your-body-smbghost-pre-auth-rce.html) + - [2] [Chompie1337's CVE-2020-0796 exploit](https://github.com/chompie1337/SMBGhost_RCE_PoC/blob/master/exploit.py) The whole series of " Getting Physical: Extreme abuse of Intel based Paging Systems" by N. Economou & E. Nissim (CoreSecurity) is a must read/watch: @@ -337,23 +347,7 @@ The whole series of " Getting Physical: Extreme abuse of Intel based Paging Syst Other useful resources: - - [3] ["What Makes It Page? The Windows 7 x64 Virtual Memory Manager" - M. Martignetti](https://www.amazon.com/What-Makes-Page-Windows-Virtual/dp/1479114294){:name="3"} + - [3] ["What Makes It Page? The Windows 7 x64 Virtual Memory Manager" - M. Martignetti](https://www.amazon.com/What-Makes-Page-Windows-Virtual/dp/1479114294) - ["Gynvael's Hacking Livestream #30: Windows Kernel Debugging Part III" - A. "honorary_bot" Shishkin](https://www.youtube.com/watch?v=7zTtVYjjquA) - ["Windows 8 Kernel Memory Protections Bypass" - J. Fetiveau](https://labs.f-secure.com/archive/windows-8-kernel-memory-protections-bypass/) -*[CPL]: Current Privilege Level -*[DPL]: Descriptor Privilege Level -*[MDL]: Memory Descriptor List -*[MMU]: Memory Management Unit -*[PA]: Physical Address -*[PAS]: Physical Address Space -*[PD]: Page Descriptor -*[PDE]: Page Descriptor Entry -*[PDPT]: Page Directory Pointer Table -*[PDPTE]: Page Directory Pointer Table Entry -*[PML4]: Page Map Level 4 -*[PML4E]: Page Map Level 4 Entry -*[PT]: Page Table -*[PTE]: Page Table Entry -*[VA]: Virtual Address -*[VAS]: Virtual Address Space diff --git a/content/2020-12-29-cheap_sandboxing_with_appcontainers.md b/content/2020-12-29-cheap_sandboxing_with_appcontainers.md index 7475a8d5..e40402ee 100644 --- a/content/2020-12-29-cheap_sandboxing_with_appcontainers.md +++ b/content/2020-12-29-cheap_sandboxing_with_appcontainers.md @@ -1,9 +1,13 @@ -date: 2020-12-29 00:00:00 -modified: 2020-12-29 00:00:00 -title: Cheap sandboxing with AppContainers -author: hugsy -category: research -tags: windows, sandbox, appcontainer ++++ +title = "Cheap sandboxing with AppContainers" +authors = ["hugsy"] +date = 2020-12-29T00:00:00Z +updated = 2020-12-29T00:00:00Z + +[taxonomies] +categories = ["research"] +tags = ["windows", "sandbox", "appcontainer"] ++++ ## Background @@ -11,23 +15,23 @@ This is a short blog post that I decided to finish recently after looking for a 1. was free/open-source & robustly tested 2. easily hackable to my need (custom permissions on file/folder/registry, on network access, on device access etc.) 3. little to no modification to my system -So off-the-shelf sandboxing products were disregarded immediately because they almost always fail on point #2 and **always** fail on point #1 as they'll tend to increase attack surface (which is kindda the opposite of the objective here). So quickly Google turned me to [Windows AppContainers](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation){:target="_blank"} which the MSDN details the [implementation](https://docs.microsoft.com/en-us/windows/win32/secauthz/implementing-an-appcontainer){:target="_blank"} well-enough. +So off-the-shelf sandboxing products were disregarded immediately because they almost always fail on point #2 and **always** fail on point #1 as they'll tend to increase attack surface (which is kindda the opposite of the objective here). So quickly Google turned me to [Windows AppContainers](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation) which the MSDN details the [implementation](https://docs.microsoft.com/en-us/windows/win32/secauthz/implementing-an-appcontainer) well-enough. AppContainers are not new and by the look of it, they are here to stay. They were introduced in Windows 8 as an in-kernel isolation mechanism (a-la seccomp for Linux) and are the default model for UWP applications which a simple look at the new Calculator in Process Hacker shows immediately: -![image_alt](/assets/images/4f110a8b-5af4-4f03-8c8d-6fe8e297fffe.png) +{{ img(src="/img/4f110a8b-5af4-4f03-8c8d-6fe8e297fffe.png" title="image_alt") }} As the MSDN mentions, AppContainers operates on 6 levels of isolation, each programmatically customizable: - - The **[File isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#file-isolation){:target="_blank"}** + - The **[File isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#file-isolation)** operates by creating for the AppContained process its own sandbox and named object subtree. This allows the kernel to finely control access to the FS by the contained process. - - The **[Network isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#network-isolation){:target="_blank"}** + - The **[Network isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#network-isolation)** will prevent any communication from/to the process over the network unless explicitly given permissions (and they have relatively explicit names, for instance `WinCapabilityInternetClientSid` to allow Internet access as a client - see [[WELL_KNOWN_SID_TYPE enumeration]](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-well_known_sid_type)) - - The **[Process isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#process-isolation){:target="_blank"}** + - The **[Process isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#process-isolation)** makes the process unable to get a handle to any process outside the sandbox - - And **[Window isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#window-isolation){:target="_blank"}** which + - And **[Window isolation](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#window-isolation)** which makes the process unable to target the Window of other processes. - - There's also [**Device isolation**](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#device-isolation){:target="_blank"} and [**Credential isolation**](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#credential-isolation){:target="_blank"} but I haven't played too much around those yet... Maybe a next post... + - There's also [**Device isolation**](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#device-isolation) and [**Credential isolation**](https://docs.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation#credential-isolation) but I haven't played too much around those yet... Maybe a next post... A useful feature added is the DllCharacteristics flag [`IMAGE_DLLCHARACTERISTICS_APPCONTAINER` - 0x1000](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#dll-characteristics) that will prevent a specific DLL image from being located outside an AppContained environment. @@ -36,7 +40,7 @@ A useful feature added is the DllCharacteristics flag [`IMAGE_DLLCHARACTERISTICS AppContainers being session specific, they are linked to the Session Id: more precisely objects of the container will reside in the `\Sessions\\AppContainerNamedObjects\` -``` +```txt lkd> dx @$cursession.Objects.Children.Where( x => x.Name == "Sessions").First().Children[2].Children @$cursession.Objects.Children.Where( x => x.Name == "Sessions").First().Children[2].Children : [object Generator] [0x0] : \Sessions\2\AppContainerNamedObjects @@ -52,12 +56,14 @@ As for file/folder objects, they will be located in `%LOCALAPPDATA%\Packages\ -
 Note: All the snippets below are C/C++ used in my [`pwn++`](https://github.com/hugsy/pwn--){:target="_blank"} library. Refer to the source code for the full implementation. Additionally, as I was already implementing my own version, I stumbled upon [@zodiacon](https://twitter.com/zodiacon){:target="_blank"}'s article[[1]](#ref_1) and implementation[[2]](#ref_2). You might prefer reading/using it if you want a serious implementation.
+{% note() %} +All the snippets below are C/C++ used in my [`pwn++`](https://github.com/hugsy/pwn--) library. Refer to the source code for the full implementation. Additionally, as I was already implementing my own version, I stumbled upon {{ twitter(user="zodiacon") }}. You might prefer reading/using it if you want a serious implementation. +{% end %} ### Create an AppContainer profile -That's as simple as it gets: there's an API exactly for that [`CreateAppContainerProfile`](https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-createappcontainerprofile){:target="_blank"} +That's as simple as it gets: there's an API exactly for that [`CreateAppContainerProfile`](https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-createappcontainerprofile) ```c++ PSID AppContainerSid; std::string ContainerName("MyContainer"); @@ -73,7 +79,7 @@ std::string ContainerName("MyContainer"); ### Add the desired capabilities -This was slightly trickier: to expose a specific capability or file/folder access to the container we must rely on [Windows object ACL mechanism](https://docs.microsoft.com/en-us/windows/win32/secauthz/modifying-the-acls-of-an-object-in-c--){:target="_blank"}. +This was slightly trickier: to expose a specific capability or file/folder access to the container we must rely on [Windows object ACL mechanism](https://docs.microsoft.com/en-us/windows/win32/secauthz/modifying-the-acls-of-an-object-in-c--). ```c++ // Saved the old ACL - you don't want to skip this step 😉 @@ -110,7 +116,7 @@ Not unlike [process reparenting](https://github.com/hugsy/pwn--/tree/main/Tools/ ### Start the process -All that's left now, is simply to invoke [`CreateProcess`](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa){:target="_blank"} and we get the AppContained process. +All that's left now, is simply to invoke [`CreateProcess`](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa) and we get the AppContained process. ```c++ ::CreateProcessW( @@ -134,7 +140,7 @@ Surprisingly not hard to implement in C/C++ (and by extension also in C#), I'm s The ~~complete~~ functional command line tool AppContainMe[[3]](#ref_3) that uses the AppContainer implementation allows to launch contained process: -``` +```txt PS> d:\code\pwn++\x64\release\appcontainme.exe [-] syntax appcontainme.exe 'process_to_run.exe arg1 arg2' [d:\allowed\path1 d:\allowed\path2] [c:Capability1 c:Capability2] [r:regkey1 r:regkey2] @@ -146,11 +152,11 @@ It's not complete but does the trick for me: without any option, the process wil PS> AppContainMe powershell ``` -![image_alt](/assets/images/29d17988-bf1f-4a1c-8b63-b01e97e6b53f.png) +{{ img(src="/img/29d17988-bf1f-4a1c-8b63-b01e97e6b53f.png" title="image_alt") }} It won't also have any network access: -``` +```txt PS C:\WINDOWS\System32\WindowsPowerShell\v1.0> Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('http://google.com')) Exception calling "DownloadString" with "1" argument(s): "The remote name could not be resolved: @@ -164,7 +170,7 @@ At line:1 char:1 Or process listing: -``` +```txt PS C:\WINDOWS\System32\WindowsPowerShell\v1.0> ps Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName @@ -173,10 +179,10 @@ Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ``` So that confirms all the "isolation" points that are stated in the MSDN. It also works perfectly well for Win32 GUI apps, for PDF Readers -![PDF Reader](/assets/images/1e3a7b9c-7ef6-481d-a803-d0a969b3eab4.png) +{{ img(src="/img/1e3a7b9c-7ef6-481d-a803-d0a969b3eab4.png" title="PDF Reader") }} Also, for having lightweight web browsing sessions (like with [`qtweb.exe`](http://www.qtweb.net/)) -![Web browser](/assets/images/283fd853-c2c7-4846-9b7c-242bfe1b02a1.png) +{{ img(src="/img/283fd853-c2c7-4846-9b7c-242bfe1b02a1.png" title="Web browser") }} That's pretty much it for this small post about AppContainers. If you want to play out-of-the-box with `AppContainMe`, a release archive with all the files is [here](https://github.com/hugsy/pwn--/releases). @@ -185,9 +191,9 @@ That's pretty much it for this small post about AppContainers. If you want to pl Shout out to Pavel Yosivovich for his article and tool on AppContainer. And credits to COVID lockdown for giving me time to get back to finishing writing articles. More to come 😉... - - [1] [Fun with AppContainers](https://scorpiosoftware.net/2019/01/15/fun-with-appcontainers/){:target="_blank"} - - [2] [zodiacon/RunAppContainer - Github](https://github.com/zodiacon/RunAppContainer){:target="_blank"} - - [3] [hugsy/pwn++ - Github](https://github.com/hugsy/pwn--/tree/main/Tools/Win32/AppContainMe){:target="_blank"} + - [1] [Fun with AppContainers](https://scorpiosoftware.net/2019/01/15/fun-with-appcontainers/) + - [2] [zodiacon/RunAppContainer - Github](https://github.com/zodiacon/RunAppContainer) + - [3] [hugsy/pwn++ - Github](https://github.com/hugsy/pwn--/tree/main/Tools/Win32/AppContainMe) Peace ✌ diff --git a/content/2021-01-10-browsing_registry_kernel_mode.md b/content/2021-01-10-browsing_registry_kernel_mode.md index 41be3f87..977937cb 100644 --- a/content/2021-01-10-browsing_registry_kernel_mode.md +++ b/content/2021-01-10-browsing_registry_kernel_mode.md @@ -1,16 +1,24 @@ -date: 2021-01-10 00:00:00 -modified: 2021-01-10 00:00:00 -title: Browsing the registry in kernel-mode -author: hugsy -tags: windows , kernel , registry , windbg -category: research ++++ +title = "Browsing the registry in kernel-mode" +authors = ["hugsy"] +date = 2021-01-10T00:00:00Z +updated = 2021-01-10T00:00:00Z -One of Windows kernel subsystem I recently dug into is the Configuration Manager (CM), mostly because I found very scarce public resources about it despite its criticality: this subsystem is responsible for managing the configuration of all Windows resources, and in user-land is exposed via a very familiar mechanism, the [Windows Registry](https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users). It is a pretty well documented [user-land mechanism](https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry), and so is its [kernel driver API](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/registry-trees-and-keys). My curiosity was around its inner working, and all the few (but brilliant) resources can be found in the link section below. +[taxonomies] +categories = ["research"] +tags = ["windows", "kernel", "registry", "windbg"] -What I wondered was: How is the registry handled in the kernel (i.e. by the CM)? So in the same way that I explored [other](/2020/06/14/playing_with_self_reference_pml4_entry/) [Windows](https://github.com/hugsy/windbg_js_scripts/blob/master/scripts/VadExplorer.js) [subsystems](/2019/01/30/playing-with-windows-root-directory-object/), I tried to keep a practical approach, and the result was [this WinDbg Js script, `RegistryExplorer.js`](#link_0) that'll be referring to throughout this post. This script allows to browse and query via LINQ the registry in a kernel debugging session. +[extra] +header_img = "/img/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png" ++++ -_Notes_: this is a collection of notes, do not blindly trust, assume mistakes. Also, you'll find the KD commands are given to reproduce easily, but your offset/index may vary. Last, everything was done/tested against Windows 10 x64 1909: I assume those findings to be applicable to other versions, but it may not be the case. +One of Windows kernel subsystem I recently dug into is the Configuration Manager ({{ abbr(abbr="CM", title="Configuration Manager") }}), mostly because I found very scarce public resources about it despite its criticality: this subsystem is responsible for managing the configuration of all Windows resources, and in user-land is exposed via a very familiar mechanism, the [Windows Registry](https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users). It is a pretty well documented [user-land mechanism](https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry), and so is its [kernel driver API](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/registry-trees-and-keys). My curiosity was around its inner working, and all the few (but brilliant) resources can be found in the link section below. +What I wondered was: How is the registry handled in the kernel by the {{ abbr(abbr="CM", title="Configuration Manager") }}? So in the same way that I explored [other](/2020/06/14/playing_with_self_reference_pml4_entry/) [Windows](https://github.com/hugsy/windbg_js_scripts/blob/master/scripts/VadExplorer.js) [subsystems](/2019/01/30/playing-with-windows-root-directory-object/), I tried to keep a practical approach, and the result was this WinDbg Js script, `RegistryExplorer.js` [^0] that'll be referring to throughout this post. This script allows to browse and query via LINQ the registry in a kernel debugging session. + +{% note() %} +This is a collection of notes, do not blindly trust, assume mistakes. Also, you'll find the KD commands are given to reproduce easily, but your offset/index may vary. Last, everything was done/tested against Windows 10 x64 1909: I assume those findings to be applicable to other versions, but it may not be the case. +{% end %} ## Overview @@ -27,18 +35,20 @@ As a tree, a __Hive__ can be browsed, exposing: Therefore a Key can contain Sub-Keys but also Values, just like a folder can contain sub-folders and files. Later on, we'll explain how to enumerate them, as we must go over some pre-requisites first. It could be noted that the analogy of a typical File System is true to the point where it is possible to abuse some situations via Symbolic Links (exploiting `REG_LINK` types) but we won't be covering that today. -
 Note: for convenience, the following equivalence will be used throughout this post:
- * Top-Level Keys = Root Keys - * Sub Keys = Keys (as long as they aren't Root Keys) +{% note(type="tip") %} +For convenience, the following equivalence will be used throughout this post: + * Top-Level Keys = Root Keys + * Sub Keys = Keys (as long as they aren't Root Keys) +{% end %} -The best structure definition of a Hive I could find comes from [Windows Kernel Internals NT Registry Implementation](#link_1) (you'll find many references to the PDF in this post). +The best structure definition of a Hive I could find comes from "Windows Kernel Internals NT Registry Implementation"[^1] (you'll find many references to the PDF in this post). -![image_alt](/assets/images/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png){:width="750px"} +{{ img(src="/img/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png", title="Hive Layout") }} -Some hives are loaded very early in the boot process, as the BCD needs to retrieve its configuration settings from it in the `BCD` hive; and also during kernel loading, hardware info are exposed from the `HARDWARE` hive. Once parsed and loaded from file to memory, all the system hives are linked via a `LIST_ENTRY` whose head is pointed by the exposed symbol `nt!CmpHiveListHead`, and can be iterated over as a list of `nt!_CMHIVE` object using the `nt!_CMHIVE.HiveList` field. Therefore a quick parsing can be done with our best friends WinDbg + DDM, which allows us to do some LINQ magic: +Some hives are loaded very early in the boot process, as the {{ abbr(abbr="BCD", title="Boot Configuration Database") }} needs to retrieve its configuration settings from it in the {{ abbr(abbr="CM", title="Boot Configuration Database") }} hive; and also during kernel loading, hardware info are exposed from the `HARDWARE` hive. Once parsed and loaded from file to memory, all the system hives are linked via a `LIST_ENTRY` whose head is pointed by the exposed symbol `nt!CmpHiveListHead`, and can be iterated over as a list of `nt!_CMHIVE` object using the `nt!_CMHIVE.HiveList` field. Therefore a quick parsing can be done with our best friends WinDbg + DDM, which allows us to do some LINQ magic: -``` +```txt 0: kd> dx -s @$hives = Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY*)&nt!CmpHiveListHead,"nt!_CMHIVE","HiveList") 0: kd> dx @$hives.Count() @@ -67,7 +77,7 @@ which looks [familiar](https://docs.microsoft.com/en-us/windows/win32/sysinfo/pr An essential pre-requisite to understand how values are accessed in the kernel, is to understand 2 critical structures: `Cells` and `Key Nodes` (for now). -From the [PDF "Windows Kernel Internals NT Registry Implementation"](#link_1), a `Cell` (p.12) is: +According to "Windows Kernel Internals NT Registry Implementation"[^1], a `Cell` (p.12) is: - The unit of storage allocation within the hive [...] - Used to store raw data, and build up logical data @@ -80,7 +90,7 @@ So when by browsing a key node, what to pay attention to are: - the SubKey list (i.e. ~_subfolders_) -``` +```txt 0: kd> dt _CM_KEY_NODE nt!_CM_KEY_NODE [...] @@ -91,7 +101,7 @@ nt!_CM_KEY_NODE - the Value list (i.e. ~_files_) -``` +```txt 0: kd> dt _CM_KEY_NODE nt!_CM_KEY_NODE [...] @@ -106,8 +116,8 @@ nt!_CHILD_LIST And looking up a specific Value can be summarized as such: -![img](https://i.imgur.com/VpAuNWf.png){:width="750px"} -[Source](#link_1) +{{ img(src="//i.imgur.com/VpAuNWf.png", title="Lookup of value `Foo`") }} +Source[^1] As we see from the symbols, Value and SubKey lists are not designated by direct pointers in memory, but instead by indexes. Those indexes point to Cells, which contains either the data itself or the next key node to parse to reach the data. We've kept mentioning `Cells` without covering it, it now becomes important to do so, know how Cells are, how they work and how they can be accessed. @@ -140,7 +150,7 @@ So the cell index is a ULONG, which can be decomposed as a bitmask that allows t Now how do we go from the key node to a cell, assuming we have a hive handle and an index? Remember above when we mentioned that the procedure to get to the cell is a function pointer inside the hive handle: `nt!_HHIVE.GetCellRoutine`? Well, that's how. Also interestingly, all the hive handles are pointing to the same function `nt!HvpGetCellPaged`, although it doesn't have to be the case: -``` +```txt 0: kd> dt _HHIVE nt!_HHIVE +0x000 Signature : Uint4B // 0xbee0bee0 @@ -213,7 +223,7 @@ It was interesting to me to find that the engineers behind the CM have decided t Now that we've understood the logic behind Cells and how to navigate through them, the rest is easier to understand. As we've mentioned before, "Key Values" are roughly the equivalent of a regular filesystem files. To get the values of a specific key node, one can use the field `nt!_CM_KEY_NODE.ValueList` (of type `_CHILD_LIST`) we've briefly discussed above. -``` +```txt 0: kd> dt _CHILD_LIST nt!_CHILD_LIST +0x000 Count : Uint4B @@ -222,7 +232,7 @@ nt!_CHILD_LIST Then it's as simple as it gets: the structure gives us the number of values and the Cell Index of the array (of the form of an array of size `_CHILD_LIST.Count` x `sizeof(HCELL_INDEX)`) of all the values of this key node. Then we simply iterate through the list of HCELL_INDEX using `GetCellAddress(KeyHive, Index)` to get the Key Nodes of type `CM_KEY_VALUE_SIGNATURE`: the type `CM_KEY_VALUE_SIGNATURE` will indicate that the current node has a structure of `nt!_CM_KEY_VALUE`, where the actual content and content length can be read. -``` +```txt 0: kd> dt _CM_KEY_VALUE nt!_CM_KEY_VALUE +0x000 Signature : Uint2B @@ -243,7 +253,7 @@ nt!_CM_KEY_VALUE By knowing how cells work it is possible to know how subkeys will be linked: subkeys are just `_CM_KEY_NODE` objects. the structure gives 2 fields -``` +```txt +0x014 SubKeyCounts : [2] Uint4B +0x01c SubKeyLists : [2] Uint4B ``` @@ -261,7 +271,7 @@ graph LR; As we shown before from the linked list of `_CMHIVE` from `nt!CmpHiveListHead` we can iterate through all the system hives. Each hive object has a pointer to a handle of hive (`_HHIVE`) which exposes a `_DUAL` field named `Storage`: the index 0 is used for permanent storage, index 1 for volatile -``` +```txt 0: kd> dt _DUAL nt!_DUAL +0x000 Length : Uint4B @@ -277,15 +287,14 @@ To summarize more graphically
graph LR; - -Z(nt!CmpHiveListHead) --> X["_CMHIVE"]; -X-- ".Hive" --> Y["_HHIVE"]; -Y-- ".Storage[0=Permanent,1=Volatile]" --> W[_HMAP_DIRECTORY] + Z(nt!CmpHiveListHead) --> X["_CMHIVE"]; + X-- ".Hive" --> Y["_HHIVE"]; + Y-- ".Storage[0=Permanent,1=Volatile]" --> W[_HMAP_DIRECTORY]
The subkeys will be located in the `Map` element (of type `_HMAP_DIRECTORY`). The `_HMAP_DIRECTORY` structure simply contains 1 element, a table of 1024 `_HMAP_TABLE`, each of them structured of 1 element: a `Table` of 512 `_HMAP_ENTRY`. -``` +```txt 0: kd> dt _HMAP_DIRECTORY nt!_HMAP_DIRECTORY +0x000 Directory : [1024] Ptr64 _HMAP_TABLE @@ -317,15 +326,17 @@ The last nibble of `PermanentBinAddress` is used for meta-data, so we can bitwis ## Put it all together -As a learning exercise, I always try to build a script/tool when digging into a topic, and here the result is another WinDbg JS script, [`RegistryExplorer.js`](#link_0) which will allow to navigate through the registry using WinDbg Debugger Data Model (and therefore also query it via LINQ) +As a learning exercise, I always try to build a script/tool when digging into a topic, and here the result is another WinDbg JS script, `RegistryExplorer.js`[^0] which will allow to navigate through the registry using WinDbg Debugger Data Model (and therefore also query it via LINQ) -![image_alt](/assets/images/5787cef5-11cc-4a1f-97b7-2f6533812b2d.png){:width="500px"} +{{ img(src="/img/5787cef5-11cc-4a1f-97b7-2f6533812b2d.png" title="image_alt") }} -
 Note: a better version was done by @msuiche [here](#link_3)
+{% note() %} +a better version was done by {{ twitter(user="msuiche") }} here[^3] +{% end %} Example: -``` +```txt 0: kd> dx @$cursession.Registry.Hives @$cursession.Registry.Hives : [object Generator] [0x0] : \REGISTRY\MACHINE\SYSTEM @@ -349,14 +360,15 @@ Example: Or the click-friendly version 😀 -![registryexplorer](/assets/images/0a76e279-63a2-4643-8f1f-bd3c877323d8.png){:width="750px"} +{{ img(src="/img/0a76e279-63a2-4643-8f1f-bd3c877323d8.png", title="RegistryExplorer.js") }} + ### Practical Toy Example: dumping SAM -Any beginner pentester would (should?*) know that in user-mode, a local Administrator account has enough privilege to dump the `SAM` & `SYSTEM` hives from the command line using `reg.exe`: (* If you didn't know, I'd suggest reading [this](#link_4) ASAP) +Any beginner pentester would (should?) know that in user-mode, a local Administrator account has enough privilege to dump the `SAM` & `SYSTEM` hives from the command line using `reg.exe`: (* If you didn't know, I'd suggest reading this[^4] ASAP) -```batch +```bat PS C:\WINDOWS\system32> reg.exe save HKLM\SAM C:\Temp\SAM.bkp The operation completed successfully. ``` @@ -372,14 +384,14 @@ And then real life strikes... As I was trying to get those values manually, the initial script failed (crashed) complaining there was an invalid access to user-mode memory: -```text +```txt GetCellDataAddress(Hive=ffffab04d1191000, Index=32): type=0 table=0 block=0 offset=32 [0x0] : Unable to read target memory at '0x280f6fa1850' in method 'readMemoryValues' [at registryexplorer (line 20 col 18)] ``` It seemed that the cells for accessing the SAM were at some points hitting user-mode area, in a process different than `System`, so the address access walking the wrong page table, and hence the exception from WinDbg. Which got immediately confirmed: -```text +```txt 0: kd> dt _hmap_entry ffffab04d1191000 nt!_HMAP_ENTRY +0x000 BlockOffset : 0 @@ -389,7 +401,7 @@ nt!_HMAP_ENTRY Then how does the kernel know where to fetch this information? Well it turned out that the hive handle can hold reference to a process in its `ViewMap.ProcessTuple` attribute, of type `_CMSI_PROCESS_TUPLE` which holds both a handle to the `_EPROCESS` and a pointer to the `_EPROCESS`. We can use that information to determine the backing process: -```text +```txt 0: kd> dt _hhive ffffab04d1191000 ViewMap.ProcessTuple nt!_HHIVE +0x0d8 ViewMap : @@ -405,7 +417,7 @@ nt!_HHIVE It points to the `Registry` process, which makes sense. To confirm, we can switch to the context of the process, and try to re-access the UM address `0x280f6fa1850`: -```text +```txt 0: kd> dx -s @$cursession.Processes.Where( x => x.Name == "Registry").First().SwitchTo() 0: kd> db 0x280f6fa1850 00000280`f6fa1850 a8 ff ff ff 6e 6b 20 00-4a 92 fb 8e 6b 38 d5 01 ....nk .J...k8.. @@ -421,7 +433,7 @@ The signature `kn` (0x6b6e) at `0x280f6fa1850+sizeof(ULONG)` confirms we're hitt Now I could access some keys & values but not everything: -```text +```txt 0: kd> dx @$SamHive = @$cursession.Registry.Hives.Where( x => x.MountPoint.EndsWith("SAM")).First() 0: kd> dx @$SamHive.RootNode.Subkeys[0].Subkeys[0].Subkeys.Where(x => x.KeyName == "Account").First().Subkeys @@ -434,7 +446,7 @@ Now I could access some keys & values but not everything: The 2nd issue faced was that when trying to access some keys in UM for the `HKLM\SAM` hive, WinDbg would inconsistently return some access violation error. This reason was somewhat easier to figure out the cause, less easy for a programmatic remediation. -```text +```txt 0: kd> dx @$cursession.Registry.Hives.Where( x => x.MountPoint.EndsWith("SAM")).First().RootNode.Subkeys[0].Subkeys[0].Subkeys.Where(x => x.KeyName == "Account").First().Subkeys[2].Subkeys @$cursession.Registry.Hives.Where( x => x.MountPoint.EndsWith("SAM")).First().RootNode.Subkeys[0].Subkeys[0].Subkeys.Where(x => x.KeyName == "Account").First().Subkeys[2].Subkeys : [object Generator] GetCellDataAddress(Hive=ffffab04d1191000, Index=8096) = 280f6fa2fa0 @@ -443,7 +455,7 @@ GetCellDataAddress(Hive=ffffab04d1191000, Index=8096) = 280f6fa2fa0 The cause behind it was not the calculation method of the Cell address but due to the fact that the page was paged out. The clue for me was the fact the missing is usually surrounded by other mapped pages. -```text +```txt 0: kd> db 280f6fa2fa0 00000280`f6fa2fa0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000280`f6fa2fb0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? @@ -457,7 +469,7 @@ The cause behind it was not the calculation method of the Cell address but due t I didn't find a way to solve this programmatically (i.e. force WinDbg to page-in), although just a reboot is enough to make sure the desired pages are still in memory. Then we can finally access the keys and values: -```text +```txt 0: kd> dx @$UserEncryptedPasswords = @$cursession.Registry.Hives.Where( x => x.MountPoint.EndsWith("SAM")).First().RootNode.Subkeys[0].Subkeys[0].Subkeys.Where(x => x.KeyName == "Account").First().Subkeys[2].Subkeys @$cursession.Registry.Hives.Where( x => x.MountPoint.EndsWith("SAM")).First().RootNode.Subkeys[0].Subkeys[0].Subkeys.Where(x => x.KeyName == "Account").First().Subkeys[2].Subkeys : [object Generator] [0x0] : 000001F4 @@ -470,7 +482,7 @@ I didn't find a way to solve this programmatically (i.e. force WinDbg to page-in So then to dump the keys for the `Administrator` (UID=500=0x1f4) -```text +```txt 0: kd> dx @$UserEncryptedPasswords[0].Values @$UserEncryptedPasswords[0].Values : [object Generator] [0x0] : F @@ -493,28 +505,20 @@ And done, we've got the data! We can now totally navigate the Registry from a KD ## Outro -Understanding those bits of the CM took more work than I imagined, but as it was nicely engineered, it was fun to go through. The CM is way more complex than that, but this is the basics: we didn't cover more advanced stuff like the use of the `.LOG` file, the memory management of the CM and other funkiness, but I hope this article was interesting and useful to you and thanks for making it this far. +Understanding those bits of the {{ abbr(abbr="CM", title="Configuration Manager") }} took more work than I imagined, but as it was nicely engineered, it was fun to go through. The {{ abbr(abbr="CM", title="Configuration Manager") }} is way more complex than that, but this is the basics: we didn't cover more advanced stuff like the use of the `.LOG` file, the memory management of the {{ abbr(abbr="CM", title="Configuration Manager") }} and other funkiness, but I hope this article was interesting and useful to you and thanks for making it this far. Peace out ✌ +## References -## Resources & Links - -Links to resources I couldn't understand anything without. - - - [0] All I could understand was compiled into my JS script [`RegistryExplorer.js`](https://github.com/hugsy/windbg_js_scripts/blob/master/scripts/RegistryExplorer.js) - - [1] [Windows Kernel Internals NT Registry Implementation](https://web.archive.org/web/20220720121211/https://ivanlef0u.fr/repo/madchat/vxdevl/papers/winsys/wk_internals/registry.pdf) - - [2] [MSDN - Registry Hives](https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-hives) - - [3] [comaeio/SwishDbgExt - Github](https://github.com/comaeio/SwishDbgExt) - - [4] [Dumping Windows Credentials - @lanjelot ](https://web.archive.org/web/20140127003901/https://www.securusglobal.com/community/2013/12/20/dumping-windows-credentials/) - - [5] [ReactOS - Github](https://github.com/reactos/reactos){:target="_blank"} - - [6] Windows Internals 6th - Part 1, Chapter 4: Management Mechanism - The Registry - - [7] [Enumerating Registry Hives](http://moyix.blogspot.com/2008/02/enumerating-registry-hives.html) - + * {{ github(user="comaeio/SwishDbgExt") }} + * {{ github(user="reactos/reactos") }} + * [Windows Internals 6th - Part 1](https://www.microsoftpressstore.com/store/windows-internals-part-1-9780735648739), Chapter 4: Management Mechanism - The Registry + * [Enumerating Registry Hives - moyix](http://moyix.blogspot.com/2008/02/enumerating-registry-hives.html) -*[CM]: Configuration Manager -*[BCD]: Boot Configuration Database -*[UM]: User-Mode -*[KM]: Kernel-Mode +[^0]: [RegistryExplorer.js](https://github.com/hugsy/windbg_js_scripts/blob/main/scripts/RegistryExplorer.js) +[^1]: [Windows Kernel Internals NT Registry Implementation](https://web.archive.org/web/20220720121211/https://ivanlef0u.fr/repo/madchat/vxdevl/papers/winsys/wk_internals/registry.pdf) +[^2]: [MSDN - Registry Hives](https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-hives) +[^4]: [Dumping Windows Credentials](https://web.archive.org/web/20140127003901/https://www.securusglobal.com/community/2013/12/20/dumping-windows-credentials/) by {{ twitter(user="lanjelot") }} diff --git a/content/2022-07-14-setup-hyperv-kdcom.md b/content/2022-07-14-setup-hyperv-kdcom.md index ed938b1f..389822ef 100644 --- a/content/2022-07-14-setup-hyperv-kdcom.md +++ b/content/2022-07-14-setup-hyperv-kdcom.md @@ -1,9 +1,13 @@ -date: 2022-07-14 00:00:00 -modified: 2022-07-14 00:00:00 -title: Setup KDCOM for 2 Hyper-V VMs -author: hugsy -category: minis -tags: windows, hyperv, kdcom ++++ +title = "Setup KDCOM for 2 Hyper-V VMs" +authors = ["hugsy"] +date = 2022-07-14T00:00:00Z +updated = 2022-07-14T00:00:00Z + +[taxonomies] +categories = ["minis"] +tags = ["windows", "hyperv", "kdcom"] ++++ How to use Hyper-V to debug using KdCOM from 2 VMs, one debugging the other. @@ -30,6 +34,6 @@ Boot the debugger and make WinDbgX listen to that port windbgx -k com:pipe,port=\\.\com1,resets=0,reconnect ``` -Enjoy +Enjoy -![image](https://user-images.githubusercontent.com/590234/179017302-76f5a1ca-acc3-48fb-a6d1-e7d13ba74a45.png) +{{ img(src="https://user-images.githubusercontent.com/590234/179017302-76f5a1ca-acc3-48fb-a6d1-e7d13ba74a45.png" title="image") }} diff --git a/content/2022-07-17-windbgx-workspaces.md b/content/2022-07-17-windbgx-workspaces.md index ff6f1556..a712a7a1 100644 --- a/content/2022-07-17-windbgx-workspaces.md +++ b/content/2022-07-17-windbgx-workspaces.md @@ -1,9 +1,13 @@ -date: 2022-07-17 00:00:00 -modified: 2022-07-17 00:00:00 -title: WinDbgX undocumented workspace options -author: hugsy -category: minis -tags: windows, windbg ++++ +title = "WinDbgX undocumented workspace options" +authors = ["hugsy"] +date = 2022-07-17T00:00:00Z +updated = 2022-07-17T00:00:00Z + +[taxonomies] +categories = ["minis"] +tags = ["windows", "windbg"] ++++ How to use WinDbgX workspaces to make debugging even easier. @@ -80,13 +84,13 @@ Here with `CryptSvc`. Also make the border red so we can find the window easily! ``` -![image](https://user-images.githubusercontent.com/590234/179410823-7b10187c-cd85-46cc-a8c5-f44ff61a5db5.png) +{{ img(src="https://user-images.githubusercontent.com/590234/179410823-7b10187c-cd85-46cc-a8c5-f44ff61a5db5.png" title="image") }} ### Setup a ARM64 Qemu debugging profile [Using EXDI](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/configuring-the-exdi-debugger-transport), and the provided `ExdiGdbSrv.dll` (in `C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2206.19001.0_x64__8wekyb3d8bbwe\amd64\ExdiGdbSrv.dll`) -``` +```bat regsvr32 ExdiGdbSrv.dll ``` diff --git a/content/2022-08-06-install-hyperv-sandbox-win10home.md b/content/2022-08-06-install-hyperv-sandbox-win10home.md index 995629e1..cfbe38fc 100644 --- a/content/2022-08-06-install-hyperv-sandbox-win10home.md +++ b/content/2022-08-06-install-hyperv-sandbox-win10home.md @@ -1,9 +1,13 @@ -date: 2022-08-06 00:00:00 -modified: 2022-08-06 00:00:00 -title: Install Hyper-V & Sandbox on Windows 10/11 Home -author: hugsy -category: minis -tags: windows, hyper-v, sandbox ++++ +title = "Install Hyper-V & Sandbox on Windows 10/11 Home" +authors = ["hugsy"] +date = 2022-08-06T00:00:00Z +updated = 2022-08-06T00:00:00Z + +[taxonomies] +categories = ["minis"] +tags = ["windows", "hyper-v", "sandbox"] ++++ Another lie, probably put in place from MS marketing team to force the hand and make more people purchase Windows 10/11 Professional licenses: Hyper-V and Windows Sandbox **can** be installed on Windows 10/11 Home Edition, not just Professional/Entreprise. Contrarily to what [even Microsoft documentation says](https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v#check-requirements), both Hyper-V and Windows Sandbox can be set in a quite simple manner, and just require an admin powershell prompt (note that a reboot will be required): @@ -13,7 +17,7 @@ Get your copy/paste skills ready! ```powershell Get-ChildItem $env:SystemRoot\Servicing\Packages\*Hyper-V*.mum | ForEach-Object { dism -Online -NoRestart -add-package:"$_" } -Enable-WindowsOptionalFeature -All -Online -LimitAccess -FeatureName Microsoft-Hyper-V +Enable-WindowsOptionalFeature -All -Online -LimitAccess -FeatureName Microsoft-Hyper-V ``` ## Install Windows Sandbox on Windows 10/11 Home @@ -23,7 +27,7 @@ Get-ChildItem $env:SystemRoot\Servicing\Packages\*DisposableClientVM*.mum | For Enable-WindowsOptionalFeature -All -Online -FeatureName Containers-DisposableClientVM ``` -![image](https://user-images.githubusercontent.com/590234/183723930-583c191c-d67a-43d1-8c5a-8c6dd6d4c78a.png) +{{ img(src="https://user-images.githubusercontent.com/590234/183723930-583c191c-d67a-43d1-8c5a-8c6dd6d4c78a.png" title="image") }} Another useful miniz! 👋 diff --git a/content/2023-04-04-section-objects-kernel-user-communication-mode.md b/content/2023-04-04-section-objects-kernel-user-communication-mode.md index e8332c54..745f8fb8 100644 --- a/content/2023-04-04-section-objects-kernel-user-communication-mode.md +++ b/content/2023-04-04-section-objects-kernel-user-communication-mode.md @@ -1,18 +1,22 @@ -title: Section Objects as Kernel/User communication mode -author: hugsy -category: research -tags: windows, hack, memory-manager -date: 2023-04-04 00:00 +0000 -modified: 2023-04-04 00:00 +0000 ++++ +title = "Section Objects as Kernel/User communication mode" +authors = ["hugsy"] +date = 2023-04-04T00:00:00Z +updated = 2023-04-04T00:00:00Z -I've recently decided to read cover to cover some Windows Internals books, and currently reading the amazing book ["What Makes It Page"](), it gave me some ideas to play with [Section Objects](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/section-objects-and-views) as they covered in great details. One thought that occurred to me was that even though a section is created from user or kernel land, its mapping can be in user-mode as much as in kernel (when called from the kernel). +[taxonomies] +categories = ["research"] +tags = ["windows", "memory-manager", "section", "backdoor"] ++++ + +I've recently decided to read cover to cover some Windows Internals books, and currently reading the amazing book ["What Makes It Page"](http://www.opening-windows.com/wmip/overview.htm), it gave me some ideas to play with [Section Objects](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/section-objects-and-views) as they covered in great details. One thought that occurred to me was that even though a section is created from user or kernel land, its mapping can be in user-mode as much as in kernel (when called from the kernel). ## Windows Section Objects For quick reminder, a Section Object on Windows is a specific type of kernel object (of structure [`nt!SECTION`](https://www.vergiliusproject.com/kernels/x64/Windows%2011/22H2%20(2022%20Update)/_SECTION)) that represents a block of memory that processes can share between themselves or between a process and the kernel. It can be mapped to the paging file (i.e. backed by memory) or to a file on disk, but either can be handled using the same set of API, and even though they are allocated by the Object Manager, it is one of the many jobs of the Memory Manager to handle their access (handle access, permission, mapping etc.). In usermode the high level API is [`kernel32!CreateFileMapping`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga), which after some hoops into `kernelbase`, boils down to [`ntdll!NtCreateSection`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntcreatesection) -![createfilemappingw](/assets/images/fc2d3446-f23b-43c9-8590-da132404c8ef.png) +{{ img(src="/img/fc2d3446-f23b-43c9-8590-da132404c8ef.png" title="createfilemappingw") }} The signature is as follow: @@ -149,7 +153,7 @@ NTSTATUS MiMapViewOfSectionCommon( ``` -What matters the most here would be the `BaseAddress` argument which will hold the UM address of the mapping. Meaning that Section Objects can be used to create communication channels between kernel <-> user mode (on top of obviously user <-> user). This is particularly nice especially because it allows to control finely the permission to the area: for instance a driver could create a section as read-writable, map its own view as RW, but expose to any process as RO. As a matter of fact, this is exactly how Windows 11 decided to protect the `(K)USER_SHARED_DATA` memory region, frequently used by kernel exploit since it's read/writable in ring-0 at a well-known address, making it a perfect way to bypass ALSR. The protection was added in 22H1 global variable which is initialized at boot-time and mapped as RW from the kernel through the `nt!MmWriteableUserSharedData`; however from user-mode only a read-only view is exposed to processes. For complete details about that protection, I invite the reader to refer to Connor McGarr's in-depth [excellent blog post](https://connormcgarr.github.io/kuser-shared-data-changes-win-11/){:target=blank} on the subject. +What matters the most here would be the `BaseAddress` argument which will hold the UM address of the mapping. Meaning that Section Objects can be used to create communication channels between kernel <-> user mode (on top of obviously user <-> user). This is particularly nice especially because it allows to control finely the permission to the area: for instance a driver could create a section as read-writable, map its own view as RW, but expose to any process as RO. As a matter of fact, this is exactly how Windows 11 decided to protect the `(K)USER_SHARED_DATA` memory region, frequently used by kernel exploit since it's read/writable in ring-0 at a well-known address, making it a perfect way to bypass ALSR. The protection was added in 22H1 global variable which is initialized at boot-time and mapped as RW from the kernel through the `nt!MmWriteableUserSharedData`; however from user-mode only a read-only view is exposed to processes. For complete details about that protection, I invite the reader to refer to Connor McGarr's in-depth [excellent blog post](https://connormcgarr.github.io/kuser-shared-data-changes-win-11/) on the subject. ## Section Object as a Kernel/User Communication Vector @@ -192,7 +196,7 @@ Where `ThreadContext` is the linear address to write the thread `CONTEXT` passed By breakpointing at the end of DriverEntry we confirm that the handle resides in the System process. -```text +```txt [*] Loading CHANGEME [+] PsGetContextThread = FFFFF8061670B5B0 [+] Section at FFFFFFFF80002FB4 @@ -202,7 +206,7 @@ MinifilterDriver+0x7275: fffff806`1aa57275 cc int 3 ``` -![windbg-output-1](/assets/images/d4b64773-6412-46dc-a9f4-f21e703e2659.png) +{{ img(src="/img/d4b64773-6412-46dc-a9f4-f21e703e2659.png" title="windbg-output-1") }} 2. Then I can use any callback (process/image notification, minifilter callbacks etc.) to invoke `ZwMapViewOfSection`, reusing the section handle from the step earlier, and `NtCurrentProcess()` as process handle. @@ -241,11 +245,11 @@ To prevent any inadverted permission drop of the view (and therefore BSoD-ing us From WinDbg we can confirm the VAD is mapped when the breakpoint is hit: -![windbg-output-2](/assets/images/03ba2044-6cd9-4efe-8570-524044a87d7f.png) +{{ img(src="/img/03ba2044-6cd9-4efe-8570-524044a87d7f.png" title="windbg-output-2") }} And as soon as the syscall returns, we're unmapped: -![sysinformer-output-1](/assets/images/748def89-0331-44bb-a112-9ded9992da45.png) +{{ img(src="/img/748def89-0331-44bb-a112-9ded9992da45.png" title="sysinformer-output-1") }} 4. Close the section in the driver unload callback. @@ -260,7 +264,7 @@ The careful reader will have notice that the step introduce a tiny race conditio When the view is created, the memory manager will create empty PTEs but expect a page fault. This is verified quickly by breaking right after the call to `ZwMapViewOfSection` -```text +```txt [*] Loading CHANGEME [+] PsGetContextThread = FFFFF8061670B5B0 [+] Section at FFFFFFFF800035E4 @@ -294,7 +298,7 @@ kd> dx -r1 @$pte2(0x000018D40BF0000).pte However, after the call to `PsGetThreadContext` the entry is correctly populated: -```text +```txt kd> g [+] Rip=00007ffa42e8d724 [+] Rbp=00000020eccff550 @@ -328,7 +332,7 @@ kd> dx -r1 @$pte2(0x000018D40BF0000) The PTE is valid: -```text +```txt kd> dx -r1 @$pte2(0x000018D40BF0000).pte @$pte2(0x000018D40BF0000).pte : PTE(PA=e23a0000, PFN=e23a0, Flags=[P RW U - - A D - -]) address : 0xd97b6f80 @@ -341,7 +345,7 @@ kd> dx -r1 @$pte2(0x000018D40BF0000).pte ``` So this means we have a great way to determine whether a physical page was accessed, using `MmGetPhysicalAddress()`. To test this we invoke it after the mapping (where we expect a null value) and a second time after the call to `PsGetThreadContext`: -![windbg-output-3](/assets/images/ac738af0-04fe-4b85-a9d2-ea3911be93cb.png) +{{ img(src="/img/ac738af0-04fe-4b85-a9d2-ea3911be93cb.png" title="windbg-output-3") }} The 2nd value for `PhyBaseAddress` points to the physical address where the function output is stored. At that point, I thought it would be sufficient to stop because we have an effective way to honeypot potential corruptions attempts: @@ -361,7 +365,7 @@ Isn't Windows awesome? There are a lot of possible fun uses of sections, and since I want to try to document more of my "stuff". Some offensive cool use case would be for instance, would be to expose code "on-demand" to a specific thread/process, removing the mapped execution page(s) from the process VAD as soon as we're done. I'll try to post follow-up updates. -For those interested in the code, you would find a minifilter driver ready to build & compile on the Github project: [ hugsy/shared-kernel-user-section-driver](https://github.com/hugsy/shared-kernel-user-section-driver){:target=blank} +For those interested in the code, you would find a minifilter driver ready to build & compile on the Github project: {{ github(user="hugsy/shared-kernel-user-section-driver") }} So, see you next time? diff --git a/content/2024-01-27-tapping-into-the-potential-of-memory-dump-emulation.md b/content/2024-01-27-tapping-into-the-potential-of-memory-dump-emulation.md index e4f76334..3ba81b47 100644 --- a/content/2024-01-27-tapping-into-the-potential-of-memory-dump-emulation.md +++ b/content/2024-01-27-tapping-into-the-potential-of-memory-dump-emulation.md @@ -1,9 +1,21 @@ -title: Tapping into the potential of Memory Dump Emulation -author: hugsy -category: research -tags: windows, hack, memory-dump, wtf, bochscpu, bochs, emulation -date: 2024-01-27 00:00 +0000 -modified: 2024-03-26 00:00 +0000 ++++ +title = "Tapping into the potential of Memory Dump Emulation" +authors = ["hugsy"] +date = 2024-01-27T00:00:00Z +updated = 2024-03-26T00:00:00Z + +aliases = [ + "/posts/2024/01/27/tapping-into-the-potential-of-memory-dump-emulation.html" +] + +[taxonomies] +categories = ["research"] +tags = ["windows", "memory-dump", "wtf", "bochscpu", "bochs", "emulation"] + +[extra] +header_img = "/img/d9e336f7-602d-4efb-8234-0630e0d54f72.png" +discuss = true ++++ This post summarizes some of the work I've been doing for the past few months during my (few) off times. Nothing new, mostly just a structured reminder for my later self. @@ -22,7 +34,7 @@ Following the well-known Feynman principle that ["what you cannot create, you do So immediately, I was stopped: originally `bochscpu` was written in Rust, `kdmp-parser` and `udmp-parser` in C++ and only `kdmp-parser` had an embryo of Python bindings (many API/structures missing, no PyPI). Perfect, so I set myself to completely dive into those libs by - creating Python bindings for `udmp-parser` and `bochscpu` - - improving the Python bindings `kdmp-parser` [originally had](https://github.com/0vercl0k/kdmp-parser/tree/3bec915e6f5304c187765be7ce3cfde713d7c29b), developed by [@masthoon](https://github.com/masthoon) + - improving the Python bindings `kdmp-parser` [originally had](https://github.com/0vercl0k/kdmp-parser/tree/3bec915e6f5304c187765be7ce3cfde713d7c29b), developed by {{ github(user="masthoon") }} At the time of this article, anyone can `pip install` any of those packages and start playing directly within the Python interpreter 3.8+ on either Windows, Linux and MacOS (since 0.1.7+) So just in order to reproduce any of the stuff mentioned below, all one would need do is: @@ -63,7 +75,7 @@ Armed with those libraries, running the emulator from a Windows kernel dump is n First from a KdNet session, you can easily create a dump at an interesting point. When looking for interesting attack surface, I like to use my own [IRP monitor tool](https://github.com/hugsy/CFB) #ShamelessSelfPromo; but for our example really anything would do, like the following: -```text +```txt kd> bp /w "@$curprocess.Name == \"explorer.exe\"" nt!NtDeviceIoControlFile [...] Breakpoint 0 hit @@ -72,13 +84,13 @@ fffff807`4f7a4670 4883ec68 sub rsp,68h ``` One way to get the dump would be using `.dump` command as such -```text +```txt kd> .dump /ka c:\temp\ActiveKDump.dmp ``` But a better way would be to use the [yrp's `bdump.js`](https://github.com/yrp604/bdump) script -```text +```txt kd> .scriptload "C:\bdump\bdump.js" [...] @@ -150,7 +162,7 @@ sess.run(hooks) sess.stop() ``` -``` console +```bat $ python kdump_runner.py Executing RIP=0xfffff80720a9d4c0 on cpu_id=0 Executing RIP=0xfffff80720a9d4c4 on cpu_id=0 @@ -269,7 +281,7 @@ int main() } ``` -![Get the dump](/assets/images/d9e336f7-602d-4efb-8234-0630e0d54f72.png) +{{ img(src="/img/d9e336f7-602d-4efb-8234-0630e0d54f72.png" title="Get the dump") }} Continuing our emulator from above, we can now invoke directly any function (here we're interested in `cryptbase!SystemFunction036`) in the dump: @@ -289,7 +301,7 @@ sess.run([hook,]) And we can successfully dump all future values: -![emulate](/assets/images/a0641b11-efdc-4d06-84af-51d404cf0ed5.png){width=50%} +{{ img(src="/img/a0641b11-efdc-4d06-84af-51d404cf0ed5.png" title="emulate") }} Same values, mission accomplished. @@ -298,7 +310,7 @@ Same values, mission accomplished. Well as the saying goes... -![same-but-different](https://media.giphy.com/media/xTiTnL7OS5ZWUUWMU0/giphy.gif){width=25%} +{{ img(src="https://media.giphy.com/media/xTiTnL7OS5ZWUUWMU0/giphy.gif" title="same-but-different") }} using [lief](https://lief-project.github.io/doc/latest/tutorials/12_elf_coredump.html) we can parse and populate the memory layout @@ -331,11 +343,11 @@ int main() ``` Compile -![Alt text](/assets/images/a31f6e3a-5c8a-40a6-8c6d-29e6b023d07a.png) +{{ img(src="/img/a31f6e3a-5c8a-40a6-8c6d-29e6b023d07a.png" title="Alt text") }} And run -![Alt text](/assets/images/8594fc66-e5da-477d-850c-1ea320c42ccf.png) +{{ img(src="/img/8594fc66-e5da-477d-850c-1ea320c42ccf.png" title="Alt text") }} and unsurprisingly, same result @@ -401,7 +413,7 @@ sess.run([hook,]) Testing with [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/) [Double-Fetch](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HEVD/Windows/DoubleFetch.c) example, immediately reveals it: -```console +```bat ❯ python .\hevd_double_fetch.py X:\hevd_double_fetch_dump\mem.dmp X:\hevd_double_fetch_dump\regs.json INFO:Parsed KernelDumpParser(X:\hevd_double_fetch_dump\mem.dmp, CompleteMemoryDump) ERROR:Possible usermode bochscpu._bochscpu.memory.AccessType.Read double fetch on VA=0x5f0008: @@ -428,7 +440,7 @@ cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b Which we can double-check with a disassembler (highlighted in magenta) -![Alt text](/assets/images/0bd46b07-e495-419d-ae11-8373868735fe.png) +{{ img(src="/img/0bd46b07-e495-419d-ae11-8373868735fe.png" title="Alt text") }} @@ -445,10 +457,10 @@ Cheers 🍻 Here are the links to those giants referred in the title: -1. [https://github.com/bochs-emu/Bochs](https://github.com/bochs-emu/Bochs) -1. [https://github.com/yrp604/bochscpu](https://github.com/yrp604/bochscpu) -1. [https://github.com/0vercl0k/wtf](https://github.com/0vercl0k/wtf) -1. [https://github.com/0vercl0k/kdmp-parser](https://github.com/0vercl0k/kdmp-parser) -1. [https://github.com/0vercl0k/udmp-parser](https://github.com/0vercl0k/udmp-parser) -1. [https://github.com/googleprojectzero/bochspwn](https://github.com/googleprojectzero/bochspwn) -1. [https://github.com/googleprojectzero/bochspwn-reloaded](https://github.com/googleprojectzero/bochspwn-reloaded) +1. {{ github(user="bochs-emu/Bochs") }} +1. {{ github(user="yrp604/bochscpu") }} +1. {{ github(user="0vercl0k/wtf") }} +1. {{ github(user="0vercl0k/kdmp-parser") }} +1. {{ github(user="0vercl0k/udmp-parser") }} +1. {{ github(user="googleprojectzero/bochspwn") }} +1. {{ github(user="googleprojectzero/bochspwn-reloaded") }} diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 00000000..a47c4147 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,6 @@ ++++ +paginate_by = 5 +sort_by = "date" +page_template = "page.html" +aliases = ["/posts/"] ++++ diff --git a/content/assets/css/overrides.css b/content/assets/css/overrides.css deleted file mode 100644 index 8abc8f69..00000000 --- a/content/assets/css/overrides.css +++ /dev/null @@ -1,73 +0,0 @@ -article -{ - font-family: Roboto; - font-style: normal; - text-align: justify; -} - -a[class="category_link"] -{ - border-radius: 25px; - border: 2px solid #73AD21; - padding: 5px; -} - -a[class="tag_link"] -{ - border-radius: 25px; - border: 2px solid #8a0e6b; - padding: 2px; -} - -div[class="alert-info"] -{ - border-radius: 15px; - border: 2px solid #0e4f95; - padding: 10px; - background-color: #1982f361; -} - -div[class="alert-success"] -{ - border-radius: 15px; - border: 2px solid #096916; - padding: 10px; - background-color: #3ce51161; -} - -div[class="alert-warning"] -{ - border-radius: 15px; - border: 2px solid #5f2405; - padding: 10px; - background-color: #b6661161; -} - -div[class="alert-error"] -{ - border-radius: 15px; - border: 2px solid #5f0505; - padding: 10px; - background-color: #670d0d61; -} - -img -{ - box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); -} - - -.post-discussions -{ - text-align: center; - position: relative; - padding: 1em 1em; - z-index: 10; - border-top: 1px solid #e5e4e1; - clear: both; -} - -.post-discussions a:hover -{ - text-decoration: underline; -} diff --git a/content/authors/_index.md b/content/authors/_index.md new file mode 100644 index 00000000..a4f30127 --- /dev/null +++ b/content/authors/_index.md @@ -0,0 +1,8 @@ ++++ +title = "authors" +description = "Author section" +paginate_by = 0 +sort_by = "title" +template = "authors.html" +page_template = "author.html" ++++ diff --git a/content/authors/hugsy.md b/content/authors/hugsy.md new file mode 100644 index 00000000..1434db04 --- /dev/null +++ b/content/authors/hugsy.md @@ -0,0 +1,15 @@ ++++ +title = "hugsy" +authors = ["hugsy"] +path = "/authors/hugsy" +aliases = ["/author/hugsy"] + +[extra] +avatar = "/img/authors/hugsy.png" +github = "hugsy" +discord = ".hugsy" +twitter = "_hugsy_" +no_discuss = true ++++ + +BWAAAAHHHH \ No newline at end of file diff --git a/content/pages/_index.md b/content/pages/_index.md new file mode 100644 index 00000000..9c02b3d7 --- /dev/null +++ b/content/pages/_index.md @@ -0,0 +1,7 @@ ++++ +page_template = "page.html" +sort_by = "none" + +[extra] +no_discuss = true ++++ \ No newline at end of file diff --git a/content/pages/about.md b/content/pages/about.md index 466d23f0..e929fa0a 100644 --- a/content/pages/about.md +++ b/content/pages/about.md @@ -1,4 +1,11 @@ -title: About ++++ +title = "About" +path = "about" +template = "about.html" + +[extra] +no_discuss = true ++++ This blog is all about technical stuff, no ads, no cookie or whatever to accept. Just content. @@ -8,34 +15,33 @@ Unless specified otherwise, all content is released under the [CopyLeft license] ## Stay in touch ## Follow us on: - -  Twitter - -  Github + - {{ twitter(user="ctf_blahcat") }} + - {{ github(user="blahcat") }} ## Recommended blogs ## ### Windows internals - - [Diary of a Reverse-Engineer](https://doar-e.github.io/){:target="_blank"} - - [windows-internals.com](https://windows-internals.com/){:target="_blank"} - - [vergiliusproject.com](https://vergiliusproject.com){:target="_blank"} - - [rayanfam.com](https://rayanfam.com/){:target="_blank"} - - [codemachine.com](https://www.codemachine.com/){:target="_blank"} - - [Geoff Chappell](https://www.geoffchappell.com/){:target="_blank"} - - [revers.engineering](https://revers.engineering/){:target="_blank"} - - [WinBindex](https://m417z.com/winbindex/){:target="_blank"} - - [tiraniddo.dev](https://www.tiraniddo.dev){:target="_blank"} - - [Redplait](http://redplait.blogspot.com/){:target="_blank"} - - [mez0.cc](https://web.archive.org/web/20230304082023/https://mez0.cc/){:target="_blank"} - - [Secret Club](https://secret.club/tags#windows){:target="_blank"} - - [Needle in a Thread Stack](https://needleinathreadstack.wordpress.com/){:target="_blank"} - - [Hyper-V Internals](https://hvinternals.blogspot.com/){:target="_blank"} - - [Old New Things](https://devblogs.microsoft.com/oldnewthing/){:target="_blank"} - -  @zodiacon's blog - -  @ivanlef0u's blog - -  @0vercl0k's blog - - @33y0re - - @SteveSyfuhs' blog - - @0xcsandker's blog - -  @j00ru's blog - - + - [Diary of a Reverse-Engineer](https://doar-e.github.io/) + - [windows-internals.com](https://windows-internals.com/) + - [vergiliusproject.com](https://vergiliusproject.com) + - [rayanfam.com](https://rayanfam.com/) + - [codemachine.com](https://www.codemachine.com/) + - [Geoff Chappell](https://www.geoffchappell.com/) + - [revers.engineering](https://revers.engineering/) + - [WinBindex](https://m417z.com/winbindex/) + - [tiraniddo.dev](https://www.tiraniddo.dev) + - [Redplait](http://redplait.blogspot.com/) + - [mez0.cc](https://web.archive.org/web/20230304082023/https://mez0.cc/) + - [Secret Club](https://secret.club/tags#windows) + - [Needle in a Thread Stack](https://needleinathreadstack.wordpress.com/) + - [Hyper-V Internals](https://hvinternals.blogspot.com/) + - [Old New Things](https://devblogs.microsoft.com/oldnewthing/) + - [Ivanlef0u's blog](https://www.ivanlef0u.tuxfamily.org) + - [Connor Mc Garr's blog](https://connormcgarr.github.io) + - {{ github(user="zodiacon")}}['s blog](https://scorpiosoftware.net/) + - {{ github(user="0vercl0k")}}[@'s blog](https://0vercl0k.tuxfamily.org/bl0g) + - {{ twitter(user="SteveSyfuhs")}}['s blog](https://syfuhs.net/) + - {{ twitter(user="0xcsandker")}}['s blog](https://csandker.io/) + - {{ twitter(user="j00ru")}} ['s blog](https://j00ru.vexillium.org/) diff --git a/content/pages/notes.md b/content/pages/notes.md index 626eea3d..5a645d7c 100644 --- a/content/pages/notes.md +++ b/content/pages/notes.md @@ -1,6 +1,13 @@ -title: Notes ++++ +title = "Notes" +path = "notes" +template = "page.html" + +[extra] +no_discuss = true ++++ A collection of notes, mostly raw, unsorted and ugly taken my research - * [Useful WinDbgX commands](https://github.com/hugsy/defcon_27_windbg_workshop/blob/fab5a4575653b0f6399493c2bdd68301d813f1fb/windbg_cheatsheet.md){:target="_blank"} + * [Useful WinDbgX commands](https://github.com/hugsy/defcon_27_windbg_workshop/blob/fab5a4575653b0f6399493c2bdd68301d813f1fb/windbg_cheatsheet.md) diff --git a/content/pages/qemu.md b/content/pages/qemu.md index 48a8f5c7..97e8ee47 100644 --- a/content/pages/qemu.md +++ b/content/pages/qemu.md @@ -1,6 +1,14 @@ -title: Qemu VM Repo -tags: linux, pwnm, debug, gef, arm, mips, aarch64, powerpc, sparc, ++++ +title = "Qemu VM Repo" +path = "qemu" +template = "page.html" +[taxonomies] +tags = ["linux", "pwn", "debug", "gef", "arm", "mips", "arm64", "powerpc", "sparc",] + +[extra] +no_discuss = true ++++ ## Non-x86 Qemu VM Repository diff --git a/content/pages/series.md b/content/pages/series.md index c78662ef..ecde018b 100644 --- a/content/pages/series.md +++ b/content/pages/series.md @@ -1,5 +1,15 @@ -title: Exploitation Series -tags: pwn ++++ +title = "Exploitation Series" +path = "series" +template = "page.html" + +[taxonomies] +tags = ["pwn"] + +[extra] +no_discuss = true ++++ + This page centralizes series of posts from this blog into their corresponding categories: @@ -19,21 +29,21 @@ This page centralizes series of posts from this blog into their corresponding ca | Id | Article | Author | | :--- | :-------------------------------------------------------------------------------------------------------------- | ----------------------: | -| 0 | [GDB Enhanced Features (GEF) tutorial : GEF 101](https://www.youtube.com/watch?v=KWG7prhH-ks){:target="_blank"} | [hugsy](/author/hugsy) | -| 1 | [Customizing GEF](https://www.youtube.com/watch?v=Pnv-FeWu4DE){:target="_blank"} | [hugsy](/author/hugsy) | -| 2 | [Automatic Runtime Analysis with GEF](https://www.youtube.com/watch?v=DoGPfi9zs6M){:target="_blank"} | [hugsy](/author/hugsy) | -| 3 | [Interfacing GEF with IDA Pro and Binary Ninja](https://www.youtube.com/watch?v=QJKmcZumWyA){:target="_blank"} | [hugsy](/author/hugsy) | -| 4 | [Creating and Using Custom structure with GEF](https://www.youtube.com/watch?v=pid2aW7Bt_w){:target="_blank"} | [hugsy](/author/hugsy) | -| 5 | [Extending GEF](https://www.youtube.com/watch?v=QsBn1nIOnWk){:target="_blank"} | [hugsy](/author/hugsy) | +| 0 | [GDB Enhanced Features (GEF) tutorial : GEF 101](https://www.youtube.com/watch?v=KWG7prhH-ks) | [hugsy](/author/hugsy) | +| 1 | [Customizing GEF](https://www.youtube.com/watch?v=Pnv-FeWu4DE) | [hugsy](/author/hugsy) | +| 2 | [Automatic Runtime Analysis with GEF](https://www.youtube.com/watch?v=DoGPfi9zs6M) | [hugsy](/author/hugsy) | +| 3 | [Interfacing GEF with IDA Pro and Binary Ninja](https://www.youtube.com/watch?v=QJKmcZumWyA) | [hugsy](/author/hugsy) | +| 4 | [Creating and Using Custom structure with GEF](https://www.youtube.com/watch?v=pid2aW7Bt_w) | [hugsy](/author/hugsy) | +| 5 | [Extending GEF](https://www.youtube.com/watch?v=QsBn1nIOnWk) | [hugsy](/author/hugsy) | ### GEF practice environment ### | Id | Environment | Author | | :--- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------: | -| 0 | [Repository of QEMU images to practice on different architectures - Mega.nz](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA){:target="_blank"} | [hugsy](/author/hugsy) | -| 1 | [Repository of QEMU images to practice on different architectures - Google Drive](https://drive.google.com/drive/folders/107uMlL_DS8yD2TS_0yrHXBDnLOj44a8P?usp=sharing){:target="_blank"} | [hugsy](/author/hugsy) | -| 2 | [Practice with GEF online](https://github.com/hugsy/gef#:~:text=Or%20try%20it%20online).){:target="_blank"} (username: `gef` password: `gef-demo`) | [hugsy](/author/hugsy) | +| 0 | [Repository of QEMU images to practice on different architectures - Mega.nz](https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA) | [hugsy](/author/hugsy) | +| 1 | [Repository of QEMU images to practice on different architectures - Google Drive](https://drive.google.com/drive/folders/107uMlL_DS8yD2TS_0yrHXBDnLOj44a8P?usp=sharing) | [hugsy](/author/hugsy) | +| 2 | [Practice with GEF online](https://github.com/hugsy/gef#:~:text=Or%20try%20it%20online).) (username: `gef` password: `gef-demo`) | [hugsy](/author/hugsy) | ## Windows Series ## @@ -43,11 +53,11 @@ This page centralizes series of posts from this blog into their corresponding ca | Id | Article | Author | | :--- | :-------------------------------------------------------------------- | ----------------------: | -| 0 | [Setting things up](#){:target="blank"} _(soon)_ | [hugsy](/author/hugsy) | -| 1 | [Windows Stack-Based Buffer Overflow](#){:target="blank"} _(soon)_ | [hugsy](/author/hugsy) | -| 2 | [Windows SEH Stack-Based Buffer Overflow](#){:target="blank"} _(todo)_ | | -| 3 | [Windows Heap Based Buffer Overflow](#){:target="blank"} _(todo)_ | | -| 4 | [Windows DLL Hijacking](#){:target="blank"} _(todo)_ | | +| 0 | [Setting things up](#) _(soon)_ | [hugsy](/author/hugsy) | +| 1 | [Windows Stack-Based Buffer Overflow](#) _(soon)_ | [hugsy](/author/hugsy) | +| 2 | [Windows SEH Stack-Based Buffer Overflow](#) _(todo)_ | | +| 3 | [Windows Heap Based Buffer Overflow](#) _(todo)_ | | +| 4 | [Windows DLL Hijacking](#) _(todo)_ | | @@ -55,17 +65,17 @@ This page centralizes series of posts from this blog into their corresponding ca | Id | Article | Author | | :--- | :-------------------------------------------------------------------------------------------------------------------------------------- | ----------------------: | -| 0 | [WinDBG cheatsheet](https://github.com/hugsy/defcon_27_windbg_workshop/blob/master/windbg_cheatsheet.md){:target="blank"} | [hugsy](/author/hugsy) | -| 1 | [Setting up a Windows VM lab for kernel debugging](/posts/2017/08/07/setting-up-a-windows-vm-lab-for-kernel-debugging){:target="blank"} | [hugsy](/author/hugsy) | -| 2 | [A Primer to Windows x64 shellcoding](/posts/2017/08/14/a-primer-to-windows-x64-shellcoding){:target="blank"} | [hugsy](/author/hugsy) | -| 3 | [First exploit in Windows Kernel (HEVD)](/posts/2017/08/18/first-exploit-in-windows-kernel-hevd){:target="blank"} | [hugsy](/author/hugsy) | -| 4 | [Arbitrary Write (aka Write-What-Where) exploits (HEVD)](/posts/2017/08/31/arbitrary-write-primitive-in-windows-kernel-hevd){:target="blank"} | [hugsy](/author/hugsy) | -| 5 | [Double-Fetch (HEVD)](#){:target="blank"} _(soon)_ | [hugsy](/author/hugsy) | +| 0 | [WinDBG cheatsheet](https://github.com/hugsy/defcon_27_windbg_workshop/blob/master/windbg_cheatsheet.md) | [hugsy](/author/hugsy) | +| 1 | [Setting up a Windows VM lab for kernel debugging](/posts/2017/08/07/setting-up-a-windows-vm-lab-for-kernel-debugging) | [hugsy](/author/hugsy) | +| 2 | [A Primer to Windows x64 shellcoding](/posts/2017/08/14/a-primer-to-windows-x64-shellcoding) | [hugsy](/author/hugsy) | +| 3 | [First exploit in Windows Kernel (HEVD)](/posts/2017/08/18/first-exploit-in-windows-kernel-hevd) | [hugsy](/author/hugsy) | +| 4 | [Arbitrary Write (aka Write-What-Where) exploits (HEVD)](/posts/2017/08/31/arbitrary-write-primitive-in-windows-kernel-hevd) | [hugsy](/author/hugsy) | +| 5 | [Double-Fetch (HEVD)](#) _(soon)_ | [hugsy](/author/hugsy) | ### Driver development ### | Id | Article | Author | | :--- | :-------------------------------------------------------------------- | ----------------------: | -| 0 | [Setting things up](#){:target="blank"} _(soon)_ | [hugsy](/author/hugsy) | -| 1 | [IO Manager, Driver & Device Objects](#){:target="blank"} _(soon)_ | [hugsy](/author/hugsy) | +| 0 | [Setting things up](#) _(soon)_ | [hugsy](/author/hugsy) | +| 1 | [IO Manager, Driver & Device Objects](#) _(soon)_ | [hugsy](/author/hugsy) | diff --git a/googleae398dde2c297744.html b/googleae398dde2c297744.html deleted file mode 100644 index e10e5843..00000000 --- a/googleae398dde2c297744.html +++ /dev/null @@ -1 +0,0 @@ -google-site-verification: googleae398dde2c297744.html \ No newline at end of file diff --git a/pelicanconf.py b/pelicanconf.py deleted file mode 100644 index abb9cb00..00000000 --- a/pelicanconf.py +++ /dev/null @@ -1,136 +0,0 @@ -AUTHOR = "hugsy" -SITENAME = "BlahCats" -SITEURL = "https://blahcat.github.io" -SITESUBTITLE = "Tales of a binary encoded life..." -PATH = "content" - -TIMEZONE = "America/Vancouver" -DEFAULT_LANG = "en" -DEFAULT_DATE = "fs" -DEFAULT_DATE_FORMAT = "%d %b %Y" - -# Feed generation is usually not desired when developing -FEED_MAX_ITEMS = 15 -FEED_ALL_ATOM = "feeds/all.atom.xml" -CATEGORY_FEED_ATOM = "feeds/{slug}.atom.xml" -TRANSLATION_FEED_ATOM = None -AUTHOR_FEED_ATOM = "feeds/author.atom.xml" -AUTHOR_FEED_RSS = None - -# Blogroll -LINKS = ( - ("TheGoonies CTF", "https://thegooniesctf.github.io/"), - ("Bernardo", "https://w00tsec.blogspot.com/"), - ("Danilo", "https://bugnotfound.com"), - ("grazfather", "http://grazfather.github.io/"), - ("Diary of a Reverse Engineer", "https://doar-e.github.io/"), - ("Connor McGarr", "https://connormcgarr.github.io"), - ("tiraniddo.dev", "https://tiraniddo.dev"), - ("zodiacon's blog", "https://scorpiosoftware.net/"), -) - -# Social widget -SOCIAL = ( - ("Twitter", "https://twitter.com/ctf_blahcat"), - ("Github", "https://github.com/blahcat"), -) - -DEFAULT_PAGINATION = 10 -PAGINATION_PATTERNS = ( - (1, "{base_name}/", "{base_name}/index.html"), - (2, "{base_name}/page/{number}/", "{base_name}/page/{number}/index.html"), -) - -DISPLAY_CATEGORIES_ON_MENU = False -DISPLAY_PAGES_ON_MENU = False - -MENUITEMS = ( - ("About", "/pages/about.html"), - ("Browse By Category", "/categories/"), - ("Browse By Tag", "/tags/"), - ("Qemu VMs", "/pages/qemu-vm-repo"), - ("Exploitation Tutorials", "/pages/exploitation-series"), - ("Misc Notes", "/pages/notes"), - ("RSS Feed", "/" + FEED_ALL_ATOM), -) - -# Uncomment following line if you want document-relative URLs when developing -# RELATIVE_URLS = True - -PLUGINS = [ - "sitemap", - "neighbors", -] - -STATIC_PATHS = [ - "assets", - "assets/images", - "keybase.txt", -] - -PYGMENTS_RST_OPTIONS = {"classprefix": "pgcss", "linenos": "inline"} - -EXTRA_PATH_METADATA = { - "assets/images/favicon.ico": {"path": "favicon.ico"}, -} - -# Sitemap plugin settings -SITEMAP = { - "format": "xml", - "priorities": {"articles": 0.5, "indexes": 0.5, "pages": 0.5}, - "changefreqs": {"articles": "monthly", "indexes": "daily", "pages": "monthly"}, -} - -# Post and Pages path -ARTICLE_URL = "posts/{date:%Y}/{date:%m}/{date:%d}/{slug}.html" -ARTICLE_SAVE_AS = "posts/{date:%Y}/{date:%m}/{date:%d}/{slug}.html" -PAGE_URL = "pages/{slug}.html" -PAGE_SAVE_AS = "pages/{slug}.html" -YEAR_ARCHIVE_SAVE_AS = "archives/{date:%Y}/index.html" -MONTH_ARCHIVE_SAVE_AS = "archives/{date:%Y}/{date:%m}/index.html" - -# Tags and Category path -CATEGORY_URL = "category/{slug}" -CATEGORY_SAVE_AS = "category/{slug}/index.html" -CATEGORIES_SAVE_AS = "categories/index.html" -TAG_URL = "tag/{slug}" -TAG_SAVE_AS = "tag/{slug}/index.html" -TAGS_SAVE_AS = "tags/index.html" -DRAFT_URL = "drafts/{slug}.html" - -# Author -AUTHOR_URL = "author/{slug}" -AUTHOR_SAVE_AS = "author/{slug}/index.html" -AUTHORS_SAVE_AS = "authors.html" - -CSS_OVERRIDE = [ - "https://fonts.googleapis.com/css?family=Roboto", - "assets/css/overrides.css", - "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css", -] - -JS_OVERRIDE = [ - "https://cdnjs.cloudflare.com/ajax/libs/mermaid/9.1.1/mermaid.min.js", -] - -# Theme settings -THEME = "attila" -HEADER_COVER = "assets/images/blog-cover.png" -AUTHORS_BIO = { - "hugsy": { - "name": "hugsy", - "cover": "https://i.imgur.com/XrHeJlW.png", - "image": "assets/images/authors/hugsy.png", - "website": "/author/hugsy", - "twitter": "_hugsy_", - "github": "hugsy", - "bio": "BWAAAAHHHH", - } -} - -COLOR_SCHEME_CSS = "github.css" - -HEADER_COVERS_BY_TAG = { - # 'cupcake': 'assets/images/rainbow_cupcake_cover.png', - # 'general':'https://casper.ghost.org/v1.0.0/images/writing.jpg' -} diff --git a/publishconf.py b/publishconf.py deleted file mode 100644 index 1493527d..00000000 --- a/publishconf.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file is only used if you use `make publish` or -# explicitly specify it as your config file. - -import os -import sys - -sys.path.append(os.curdir) -from pelicanconf import * - -# If your site is available via HTTPS, make sure SITEURL begins with https:// -SITEURL = "https://blahcat.github.io" -RELATIVE_URLS = False - -FEED_ALL_ATOM = "feeds/all.atom.xml" -CATEGORY_FEED_ATOM = "feeds/{slug}.atom.xml" - -DELETE_OUTPUT_DIRECTORY = True - -# Following items are often useful when publishing - -# DISQUS_SITENAME = "" -# GOOGLE_ANALYTICS = "" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4301886c..00000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -pelican==4.7.2 -pelican-sitemap==1.0.2 -Markdown==3.3.7 -ghp-import==2.1.0 -pelican-neighbors==1.2.0 diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index dc1536fb..00000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests -bs4 diff --git a/static/css/clean-blog.css b/static/css/clean-blog.css new file mode 100644 index 00000000..1f60849a --- /dev/null +++ b/static/css/clean-blog.css @@ -0,0 +1,443 @@ +body { + font-size: 20px; + color: #212529; + font-family: 'Roboto', 'Lora', 'Times New Roman', serif +} + +p { + line-height: 1.5; + margin: 30px 0 +} + +p a { + text-decoration: underline +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 800; + font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif +} + +a { + color: #212529; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + transition: all 0.2s +} + +a:focus, +a:hover { + color: #0085A1 +} + +blockquote { + font-style: italic; + color: #868e96 +} + +.section-heading { + font-size: 36px; + font-weight: 700; + margin-top: 60px +} + +.caption { + font-size: 14px; + font-style: italic; + display: block; + margin: 0; + padding: 10px; + text-align: center; + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px +} + +::-moz-selection { + color: #fff; + background: #0085A1; + text-shadow: none +} + +::selection { + color: #fff; + background: #0085A1; + text-shadow: none +} + +img::selection { + color: #fff; + background: transparent +} + +img::-moz-selection { + color: #fff; + background: transparent +} + +#mainNav { + position: absolute; + border-bottom: 1px solid #e9ecef; + background-color: white; + font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif +} + +#mainNav .navbar-brand { + font-weight: 800; + color: #343a40 +} + +#mainNav .navbar-toggler { + font-size: 12px; + font-weight: 800; + padding: 13px; + text-transform: uppercase; + color: #343a40 +} + +#mainNav .navbar-nav>li.nav-item>a { + font-size: 12px; + font-weight: 800; + letter-spacing: 1px; + text-transform: uppercase +} + +@media only screen and (min-width: 992px) { + #mainNav { + border-bottom: 1px solid transparent; + background: transparent + } + + #mainNav .navbar-brand { + padding: 10px 20px; + color: #fff + } + + #mainNav .navbar-brand:focus, + #mainNav .navbar-brand:hover { + color: rgba(255, 255, 255, 0.8) + } + + #mainNav .navbar-nav>li.nav-item>a { + padding: 10px 20px; + color: #fff + } + + #mainNav .navbar-nav>li.nav-item>a:focus, + #mainNav .navbar-nav>li.nav-item>a:hover { + color: rgba(255, 255, 255, 0.8) + } +} + +@media only screen and (min-width: 992px) { + #mainNav { + -webkit-transition: background-color 0.2s; + -moz-transition: background-color 0.2s; + transition: background-color 0.2s; + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + -webkit-backface-visibility: hidden + } + + #mainNav.is-fixed { + position: fixed; + top: -67px; + -webkit-transition: -webkit-transform 0.2s; + -moz-transition: -moz-transform 0.2s; + transition: transform 0.2s; + border-bottom: 1px solid #fff; + background-color: rgba(255, 255, 255, 0.9) + } + + #mainNav.is-fixed .navbar-brand { + color: #212529 + } + + #mainNav.is-fixed .navbar-brand:focus, + #mainNav.is-fixed .navbar-brand:hover { + color: #0085A1 + } + + #mainNav.is-fixed .navbar-nav>li.nav-item>a { + color: #212529 + } + + #mainNav.is-fixed .navbar-nav>li.nav-item>a:focus, + #mainNav.is-fixed .navbar-nav>li.nav-item>a:hover { + color: #0085A1 + } + + #mainNav.is-visible { + -webkit-transform: translate3d(0, 100%, 0); + -moz-transform: translate3d(0, 100%, 0); + -ms-transform: translate3d(0, 100%, 0); + -o-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) + } +} + +header.masthead { + margin-bottom: 50px; + background: no-repeat center center; + background-color: #868e96; + background-attachment: scroll; + position: relative; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover +} + +header.masthead .overlay { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + background-color: #212529; + opacity: 0.5 +} + +header.masthead .page-heading, +header.masthead .post-heading, +header.masthead .site-heading { + padding: 200px 0 150px; + color: white +} + +@media only screen and (min-width: 768px) { + + header.masthead .page-heading, + header.masthead .post-heading, + header.masthead .site-heading { + padding: 200px 0 + } +} + +header.masthead .page-heading, +header.masthead .site-heading { + text-align: center +} + +header.masthead .page-heading h1, +header.masthead .site-heading h1 { + font-size: 50px; + margin-top: 0 +} + +header.masthead .page-heading .subheading, +header.masthead .site-heading .subheading { + font-size: 24px; + font-weight: 300; + line-height: 1.1; + display: block; + margin: 10px 0 0; + font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif +} + +@media only screen and (min-width: 768px) { + + header.masthead .page-heading h1, + header.masthead .site-heading h1 { + font-size: 80px + } +} + +header.masthead .post-heading h1 { + font-size: 35px +} + +header.masthead .post-heading .meta, +header.masthead .post-heading .subheading { + line-height: 1.1; + display: block +} + +header.masthead .post-heading .subheading { + font-size: 24px; + font-weight: 600; + margin: 10px 0 30px; + font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif +} + +header.masthead .post-heading .meta { + font-size: 20px; + font-weight: 300; + font-style: italic; + font-family: 'Roboto', 'Lora', 'Times New Roman', serif +} + +header.masthead .post-heading .meta a { + color: #fff +} + +@media only screen and (min-width: 768px) { + header.masthead .post-heading h1 { + font-size: 55px + } + + header.masthead .post-heading .subheading { + font-size: 30px + } +} + +.post-preview>a { + color: #212529 +} + +.post-preview>a:focus, +.post-preview>a:hover { + text-decoration: none; + color: #0085A1 +} + +.post-preview>a>.post-title { + font-size: 30px; + margin-top: 30px; + margin-bottom: 10px +} + +.post-preview>a>.post-subtitle { + font-weight: 300; + margin: 0 0 10px +} + +.post-preview>.post-summary { + font-size: 18px +} + +.post-preview>.post-meta { + font-size: 18px; + font-style: italic; + margin-top: 0; + color: #868e96 +} + +.post-preview>.post-meta>a { + text-decoration: none; + color: #212529 +} + +.post-preview>.post-meta>a:focus, +.post-preview>.post-meta>a:hover { + text-decoration: underline; + color: #0085A1 +} + +@media only screen and (min-width: 768px) { + .post-preview>a>.post-title { + font-size: 36px + } +} + +.floating-label-form-group { + font-size: 14px; + position: relative; + margin-bottom: 0; + padding-bottom: 0.5em; + border-bottom: 1px solid #dee2e6 +} + +.floating-label-form-group input, +.floating-label-form-group textarea { + font-size: 1.5em; + position: relative; + z-index: 1; + padding: 0; + resize: none; + border: none; + border-radius: 0; + background: none; + box-shadow: none !important; + font-family: 'Roboto', 'Lora', 'Times New Roman', serif +} + +.floating-label-form-group input::-webkit-input-placeholder, +.floating-label-form-group textarea::-webkit-input-placeholder { + color: #868e96; + font-family: 'Roboto', 'Lora', 'Times New Roman', serif +} + +.floating-label-form-group label { + font-size: 0.85em; + line-height: 1.764705882em; + position: relative; + z-index: 0; + top: 2em; + display: block; + margin: 0; + -webkit-transition: top 0.3s ease, opacity 0.3s ease; + -moz-transition: top 0.3s ease, opacity 0.3s ease; + -ms-transition: top 0.3s ease, opacity 0.3s ease; + transition: top 0.3s ease, opacity 0.3s ease; + vertical-align: middle; + vertical-align: baseline; + opacity: 0 +} + +.floating-label-form-group .help-block { + margin: 15px 0 +} + +.floating-label-form-group-with-value label { + top: 0; + opacity: 1 +} + +.floating-label-form-group-with-focus label { + color: #0085A1 +} + +form .form-group:first-child .floating-label-form-group { + border-top: 1px solid #dee2e6 +} + +footer { + padding: 50px 0 65px +} + +footer .list-inline { + margin: 0; + padding: 0 +} + +footer .copyright { + font-size: 14px; + margin-bottom: 0; + text-align: center +} + +.btn { + font-size: 14px; + font-weight: 800; + padding: 15px 25px; + letter-spacing: 1px; + text-transform: uppercase; + border-radius: 0; + font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif +} + +.btn-primary { + background-color: #0085A1; + border-color: #0085A1 +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active { + color: #fff; + background-color: #00657b !important; + border-color: #00657b !important +} + +.btn-lg { + font-size: 16px; + padding: 25px 35px +} \ No newline at end of file diff --git a/static/css/overrides.css b/static/css/overrides.css new file mode 100644 index 00000000..a56bd7b1 --- /dev/null +++ b/static/css/overrides.css @@ -0,0 +1,132 @@ +body { + font-family: Roboto; + font-style: normal; + text-align: justify; +} + +pre code { + font-family: + "Consolas", + "Liberation Mono", + "Courier New", + monospace; + font-style: normal; + font-size: 0.80em; +} + +article { + font-family: Roboto; + font-style: normal; + text-align: justify; +} + +a[class="category_link"] { + border-radius: 25px; + border: 2px solid #73AD21; + padding: 5px; +} + +a[class="tag_link"] { + border-radius: 25px; + border: 2px solid #8a0e6b; + padding: 2px; +} + +div[class="alert-info"] { + border-radius: 15px; + border: 1px solid #0e4f95; + padding: 5px; + background-color: #1982f361; +} + +div[class="alert-success"] { + border-radius: 15px; + border: 1px solid #096916; + padding: 5px; + background-color: #3ce51161; +} + +div[class="alert-warning"] { + border-radius: 15px; + border: 2px solid #5f2405; + padding: 5px; + background-color: #b6661161; +} + +div[class="alert-error"] { + border-radius: 15px; + border: 2px solid #5f0505; + padding: 5px; + background-color: #670d0d61; +} + +img { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); +} + +.post-tags { + font-size: 0.80em; + border-top: 1px solid #e5e4e1; +} + +.post-tags a { + display: inline-block; + /* margin: 0 0.333em 0.666em 0; */ + padding: 0.575em 1em; + background: #fff; + border: 1px solid #094707; + font-size: 0.75em; + font-weight: 500; + line-height: 1.333em; + text-align: center; + text-decoration: none; + color: #094707; + border-radius: 2em; + transition: all ease 0.3s; + box-sizing: border-box +} + +.post-tags a:hover, +.post-tags a:focus { + background: #f26a3d; + color: #fff +} + +.post-discussion { + text-align: center; + position: relative; + padding: 1em 1em; + font-size: 0.80em; + z-index: 5; + border-top: 1px solid #e5e4e1; + clear: both; +} + +.post-discussions a:hover { + text-decoration: underline; +} + +.post-nav { + position: relative; + border-top: 1px solid #e5e4e1; + font-size: 0.80em; + margin-top: 10px; +} + +.post-nav-right, +.post-nav-prev { + font-size: 0.90em; +} + +.post-nav a:hover { + text-decoration: none; +} + +.post-nav-teaser { + padding: 1.5em 0; + min-height: 6em; +} + +.post-nav-next .post-nav-teaser { + padding-right: 1em; +} \ No newline at end of file diff --git a/content/assets/images/03ba2044-6cd9-4efe-8570-524044a87d7f.png b/static/img/03ba2044-6cd9-4efe-8570-524044a87d7f.png similarity index 100% rename from content/assets/images/03ba2044-6cd9-4efe-8570-524044a87d7f.png rename to static/img/03ba2044-6cd9-4efe-8570-524044a87d7f.png diff --git a/content/assets/images/0a76e279-63a2-4643-8f1f-bd3c877323d8.png b/static/img/0a76e279-63a2-4643-8f1f-bd3c877323d8.png similarity index 100% rename from content/assets/images/0a76e279-63a2-4643-8f1f-bd3c877323d8.png rename to static/img/0a76e279-63a2-4643-8f1f-bd3c877323d8.png diff --git a/content/assets/images/0bd46b07-e495-419d-ae11-8373868735fe.png b/static/img/0bd46b07-e495-419d-ae11-8373868735fe.png similarity index 100% rename from content/assets/images/0bd46b07-e495-419d-ae11-8373868735fe.png rename to static/img/0bd46b07-e495-419d-ae11-8373868735fe.png diff --git a/content/assets/images/1e3a7b9c-7ef6-481d-a803-d0a969b3eab4.png b/static/img/1e3a7b9c-7ef6-481d-a803-d0a969b3eab4.png similarity index 100% rename from content/assets/images/1e3a7b9c-7ef6-481d-a803-d0a969b3eab4.png rename to static/img/1e3a7b9c-7ef6-481d-a803-d0a969b3eab4.png diff --git a/content/assets/images/283fd853-c2c7-4846-9b7c-242bfe1b02a1.png b/static/img/283fd853-c2c7-4846-9b7c-242bfe1b02a1.png similarity index 100% rename from content/assets/images/283fd853-c2c7-4846-9b7c-242bfe1b02a1.png rename to static/img/283fd853-c2c7-4846-9b7c-242bfe1b02a1.png diff --git a/content/assets/images/29d17988-bf1f-4a1c-8b63-b01e97e6b53f.png b/static/img/29d17988-bf1f-4a1c-8b63-b01e97e6b53f.png similarity index 100% rename from content/assets/images/29d17988-bf1f-4a1c-8b63-b01e97e6b53f.png rename to static/img/29d17988-bf1f-4a1c-8b63-b01e97e6b53f.png diff --git a/content/assets/images/4f110a8b-5af4-4f03-8c8d-6fe8e297fffe.png b/static/img/4f110a8b-5af4-4f03-8c8d-6fe8e297fffe.png similarity index 100% rename from content/assets/images/4f110a8b-5af4-4f03-8c8d-6fe8e297fffe.png rename to static/img/4f110a8b-5af4-4f03-8c8d-6fe8e297fffe.png diff --git a/content/assets/images/5787cef5-11cc-4a1f-97b7-2f6533812b2d.png b/static/img/5787cef5-11cc-4a1f-97b7-2f6533812b2d.png similarity index 100% rename from content/assets/images/5787cef5-11cc-4a1f-97b7-2f6533812b2d.png rename to static/img/5787cef5-11cc-4a1f-97b7-2f6533812b2d.png diff --git a/content/assets/images/748def89-0331-44bb-a112-9ded9992da45.png b/static/img/748def89-0331-44bb-a112-9ded9992da45.png similarity index 100% rename from content/assets/images/748def89-0331-44bb-a112-9ded9992da45.png rename to static/img/748def89-0331-44bb-a112-9ded9992da45.png diff --git a/content/assets/images/8594fc66-e5da-477d-850c-1ea320c42ccf.png b/static/img/8594fc66-e5da-477d-850c-1ea320c42ccf.png similarity index 100% rename from content/assets/images/8594fc66-e5da-477d-850c-1ea320c42ccf.png rename to static/img/8594fc66-e5da-477d-850c-1ea320c42ccf.png diff --git a/content/assets/images/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png b/static/img/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png similarity index 100% rename from content/assets/images/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png rename to static/img/950bbc05-e57e-4d49-96a4-9aefec9a8ef6.png diff --git a/content/assets/images/CDA9D98DF912DE08CB61AD0A3A148723A37BC3F3.png b/static/img/CDA9D98DF912DE08CB61AD0A3A148723A37BC3F3.png similarity index 100% rename from content/assets/images/CDA9D98DF912DE08CB61AD0A3A148723A37BC3F3.png rename to static/img/CDA9D98DF912DE08CB61AD0A3A148723A37BC3F3.png diff --git a/content/assets/images/a0641b11-efdc-4d06-84af-51d404cf0ed5.png b/static/img/a0641b11-efdc-4d06-84af-51d404cf0ed5.png similarity index 100% rename from content/assets/images/a0641b11-efdc-4d06-84af-51d404cf0ed5.png rename to static/img/a0641b11-efdc-4d06-84af-51d404cf0ed5.png diff --git a/content/assets/images/a31f6e3a-5c8a-40a6-8c6d-29e6b023d07a.png b/static/img/a31f6e3a-5c8a-40a6-8c6d-29e6b023d07a.png similarity index 100% rename from content/assets/images/a31f6e3a-5c8a-40a6-8c6d-29e6b023d07a.png rename to static/img/a31f6e3a-5c8a-40a6-8c6d-29e6b023d07a.png diff --git a/content/assets/images/about-bg.jpg b/static/img/about-bg.jpg similarity index 100% rename from content/assets/images/about-bg.jpg rename to static/img/about-bg.jpg diff --git a/content/assets/images/ac738af0-04fe-4b85-a9d2-ea3911be93cb.png b/static/img/ac738af0-04fe-4b85-a9d2-ea3911be93cb.png similarity index 100% rename from content/assets/images/ac738af0-04fe-4b85-a9d2-ea3911be93cb.png rename to static/img/ac738af0-04fe-4b85-a9d2-ea3911be93cb.png diff --git a/content/assets/images/authors/hugsy.png b/static/img/authors/hugsy.png similarity index 100% rename from content/assets/images/authors/hugsy.png rename to static/img/authors/hugsy.png diff --git a/content/assets/images/blog-cover.png b/static/img/blog-cover.png similarity index 100% rename from content/assets/images/blog-cover.png rename to static/img/blog-cover.png diff --git a/content/assets/images/canary-header.png b/static/img/canary-header.png similarity index 100% rename from content/assets/images/canary-header.png rename to static/img/canary-header.png diff --git a/content/assets/images/contact-bg.jpg b/static/img/contact-bg.jpg similarity index 100% rename from content/assets/images/contact-bg.jpg rename to static/img/contact-bg.jpg diff --git a/content/assets/images/d4b64773-6412-46dc-a9f4-f21e703e2659.png b/static/img/d4b64773-6412-46dc-a9f4-f21e703e2659.png similarity index 100% rename from content/assets/images/d4b64773-6412-46dc-a9f4-f21e703e2659.png rename to static/img/d4b64773-6412-46dc-a9f4-f21e703e2659.png diff --git a/content/assets/images/d9e336f7-602d-4efb-8234-0630e0d54f72.png b/static/img/d9e336f7-602d-4efb-8234-0630e0d54f72.png similarity index 100% rename from content/assets/images/d9e336f7-602d-4efb-8234-0630e0d54f72.png rename to static/img/d9e336f7-602d-4efb-8234-0630e0d54f72.png diff --git a/content/assets/images/f4300721f56d68c92db76aa03c3bbd54.png b/static/img/f4300721f56d68c92db76aa03c3bbd54.png similarity index 100% rename from content/assets/images/f4300721f56d68c92db76aa03c3bbd54.png rename to static/img/f4300721f56d68c92db76aa03c3bbd54.png diff --git a/content/assets/images/f7803990-4baa-4a9a-a09b-0cde30694fa6.png b/static/img/f7803990-4baa-4a9a-a09b-0cde30694fa6.png similarity index 100% rename from content/assets/images/f7803990-4baa-4a9a-a09b-0cde30694fa6.png rename to static/img/f7803990-4baa-4a9a-a09b-0cde30694fa6.png diff --git a/content/assets/images/favicon.ico b/static/img/favicon.ico similarity index 100% rename from content/assets/images/favicon.ico rename to static/img/favicon.ico diff --git a/content/assets/images/fc2d3446-f23b-43c9-8590-da132404c8ef.png b/static/img/fc2d3446-f23b-43c9-8590-da132404c8ef.png similarity index 100% rename from content/assets/images/fc2d3446-f23b-43c9-8590-da132404c8ef.png rename to static/img/fc2d3446-f23b-43c9-8590-da132404c8ef.png diff --git a/content/assets/images/flareon-2017-header.png b/static/img/flareon-2017-header.png similarity index 100% rename from content/assets/images/flareon-2017-header.png rename to static/img/flareon-2017-header.png diff --git a/content/assets/images/flareon-2017/018ab4320dc95fa3b751227369cd27f7ee759579323d695c2453bcf9966179e0.png b/static/img/flareon-2017/018ab4320dc95fa3b751227369cd27f7ee759579323d695c2453bcf9966179e0.png similarity index 100% rename from content/assets/images/flareon-2017/018ab4320dc95fa3b751227369cd27f7ee759579323d695c2453bcf9966179e0.png rename to static/img/flareon-2017/018ab4320dc95fa3b751227369cd27f7ee759579323d695c2453bcf9966179e0.png diff --git a/content/assets/images/flareon-2017/01c609d44427749a2caa64d7cb8ae54f41788be7313e2c94fd9cd8f65476cc9c.png b/static/img/flareon-2017/01c609d44427749a2caa64d7cb8ae54f41788be7313e2c94fd9cd8f65476cc9c.png similarity index 100% rename from content/assets/images/flareon-2017/01c609d44427749a2caa64d7cb8ae54f41788be7313e2c94fd9cd8f65476cc9c.png rename to static/img/flareon-2017/01c609d44427749a2caa64d7cb8ae54f41788be7313e2c94fd9cd8f65476cc9c.png diff --git a/content/assets/images/flareon-2017/032ff5768ccf900dd61f0f87123a4257934e71e30e248ec5e55126bce1f815d8.png b/static/img/flareon-2017/032ff5768ccf900dd61f0f87123a4257934e71e30e248ec5e55126bce1f815d8.png similarity index 100% rename from content/assets/images/flareon-2017/032ff5768ccf900dd61f0f87123a4257934e71e30e248ec5e55126bce1f815d8.png rename to static/img/flareon-2017/032ff5768ccf900dd61f0f87123a4257934e71e30e248ec5e55126bce1f815d8.png diff --git a/content/assets/images/flareon-2017/05d0733685c70aa9802ace1c97c240ace73a3c18c941219d975775cae32d10a5.png b/static/img/flareon-2017/05d0733685c70aa9802ace1c97c240ace73a3c18c941219d975775cae32d10a5.png similarity index 100% rename from content/assets/images/flareon-2017/05d0733685c70aa9802ace1c97c240ace73a3c18c941219d975775cae32d10a5.png rename to static/img/flareon-2017/05d0733685c70aa9802ace1c97c240ace73a3c18c941219d975775cae32d10a5.png diff --git a/content/assets/images/flareon-2017/090d3fe35ac25fcec9052f0e216f72c75da6d96a367abe4451d04ff0af7ad5cd.png b/static/img/flareon-2017/090d3fe35ac25fcec9052f0e216f72c75da6d96a367abe4451d04ff0af7ad5cd.png similarity index 100% rename from content/assets/images/flareon-2017/090d3fe35ac25fcec9052f0e216f72c75da6d96a367abe4451d04ff0af7ad5cd.png rename to static/img/flareon-2017/090d3fe35ac25fcec9052f0e216f72c75da6d96a367abe4451d04ff0af7ad5cd.png diff --git a/content/assets/images/flareon-2017/0d1da3b02573a0f2c451b9cf801355666639e4454e26ea138b1836bdd969b36e.png b/static/img/flareon-2017/0d1da3b02573a0f2c451b9cf801355666639e4454e26ea138b1836bdd969b36e.png similarity index 100% rename from content/assets/images/flareon-2017/0d1da3b02573a0f2c451b9cf801355666639e4454e26ea138b1836bdd969b36e.png rename to static/img/flareon-2017/0d1da3b02573a0f2c451b9cf801355666639e4454e26ea138b1836bdd969b36e.png diff --git a/content/assets/images/flareon-2017/0fbe4ce9c0d1295088fa6938b36081272c976a99ca80fef5f27ec3c89ea0cafb.png b/static/img/flareon-2017/0fbe4ce9c0d1295088fa6938b36081272c976a99ca80fef5f27ec3c89ea0cafb.png similarity index 100% rename from content/assets/images/flareon-2017/0fbe4ce9c0d1295088fa6938b36081272c976a99ca80fef5f27ec3c89ea0cafb.png rename to static/img/flareon-2017/0fbe4ce9c0d1295088fa6938b36081272c976a99ca80fef5f27ec3c89ea0cafb.png diff --git a/content/assets/images/flareon-2017/17161f3635f37c0b278c18262e4a29eb4f21675316ff9a086557e390ca3be67e.png b/static/img/flareon-2017/17161f3635f37c0b278c18262e4a29eb4f21675316ff9a086557e390ca3be67e.png similarity index 100% rename from content/assets/images/flareon-2017/17161f3635f37c0b278c18262e4a29eb4f21675316ff9a086557e390ca3be67e.png rename to static/img/flareon-2017/17161f3635f37c0b278c18262e4a29eb4f21675316ff9a086557e390ca3be67e.png diff --git a/content/assets/images/flareon-2017/18a58c8dbdd8b039dc0b8492474e2ae4c0180ecc2e88a26f2d5708059aee9d4b.png b/static/img/flareon-2017/18a58c8dbdd8b039dc0b8492474e2ae4c0180ecc2e88a26f2d5708059aee9d4b.png similarity index 100% rename from content/assets/images/flareon-2017/18a58c8dbdd8b039dc0b8492474e2ae4c0180ecc2e88a26f2d5708059aee9d4b.png rename to static/img/flareon-2017/18a58c8dbdd8b039dc0b8492474e2ae4c0180ecc2e88a26f2d5708059aee9d4b.png diff --git a/content/assets/images/flareon-2017/202dd92a07c692ff036fd5b27d7ff1c85f1af93cd33007abf2fb31bd44498270.png b/static/img/flareon-2017/202dd92a07c692ff036fd5b27d7ff1c85f1af93cd33007abf2fb31bd44498270.png similarity index 100% rename from content/assets/images/flareon-2017/202dd92a07c692ff036fd5b27d7ff1c85f1af93cd33007abf2fb31bd44498270.png rename to static/img/flareon-2017/202dd92a07c692ff036fd5b27d7ff1c85f1af93cd33007abf2fb31bd44498270.png diff --git a/content/assets/images/flareon-2017/27081ef6ec0315058001a824dc711695d8a30345e055d72f079a14be522ad3f3.png b/static/img/flareon-2017/27081ef6ec0315058001a824dc711695d8a30345e055d72f079a14be522ad3f3.png similarity index 100% rename from content/assets/images/flareon-2017/27081ef6ec0315058001a824dc711695d8a30345e055d72f079a14be522ad3f3.png rename to static/img/flareon-2017/27081ef6ec0315058001a824dc711695d8a30345e055d72f079a14be522ad3f3.png diff --git a/content/assets/images/flareon-2017/304507dabd5f847b7beafec89b19e225540db7649cedfc0e2ebe4703df14a06b.png b/static/img/flareon-2017/304507dabd5f847b7beafec89b19e225540db7649cedfc0e2ebe4703df14a06b.png similarity index 100% rename from content/assets/images/flareon-2017/304507dabd5f847b7beafec89b19e225540db7649cedfc0e2ebe4703df14a06b.png rename to static/img/flareon-2017/304507dabd5f847b7beafec89b19e225540db7649cedfc0e2ebe4703df14a06b.png diff --git a/content/assets/images/flareon-2017/31858c353e03ccebb4bf63753f16ac096d38152a5ded8fe4d73dbbb00fc79c23.png b/static/img/flareon-2017/31858c353e03ccebb4bf63753f16ac096d38152a5ded8fe4d73dbbb00fc79c23.png similarity index 100% rename from content/assets/images/flareon-2017/31858c353e03ccebb4bf63753f16ac096d38152a5ded8fe4d73dbbb00fc79c23.png rename to static/img/flareon-2017/31858c353e03ccebb4bf63753f16ac096d38152a5ded8fe4d73dbbb00fc79c23.png diff --git a/content/assets/images/flareon-2017/3321b96da80e52cd9e26eda05122bb1bd58a18216d6aeb1b4205162d2ed6dbf6.png b/static/img/flareon-2017/3321b96da80e52cd9e26eda05122bb1bd58a18216d6aeb1b4205162d2ed6dbf6.png similarity index 100% rename from content/assets/images/flareon-2017/3321b96da80e52cd9e26eda05122bb1bd58a18216d6aeb1b4205162d2ed6dbf6.png rename to static/img/flareon-2017/3321b96da80e52cd9e26eda05122bb1bd58a18216d6aeb1b4205162d2ed6dbf6.png diff --git a/content/assets/images/flareon-2017/33d2e78e17819a705d01a9c9c0412090361e7ad02beb4692106996ac8e832f7b.png b/static/img/flareon-2017/33d2e78e17819a705d01a9c9c0412090361e7ad02beb4692106996ac8e832f7b.png similarity index 100% rename from content/assets/images/flareon-2017/33d2e78e17819a705d01a9c9c0412090361e7ad02beb4692106996ac8e832f7b.png rename to static/img/flareon-2017/33d2e78e17819a705d01a9c9c0412090361e7ad02beb4692106996ac8e832f7b.png diff --git a/content/assets/images/flareon-2017/3a9c0d57e4c77e2407dbe101f41a6ce6f20b65f724f9e133ea47dccdf1d17ae5.png b/static/img/flareon-2017/3a9c0d57e4c77e2407dbe101f41a6ce6f20b65f724f9e133ea47dccdf1d17ae5.png similarity index 100% rename from content/assets/images/flareon-2017/3a9c0d57e4c77e2407dbe101f41a6ce6f20b65f724f9e133ea47dccdf1d17ae5.png rename to static/img/flareon-2017/3a9c0d57e4c77e2407dbe101f41a6ce6f20b65f724f9e133ea47dccdf1d17ae5.png diff --git a/content/assets/images/flareon-2017/489d77b797f222ef52533b5da295fd7e733c9156ec43cbd44aa1f8163ece1f81.png b/static/img/flareon-2017/489d77b797f222ef52533b5da295fd7e733c9156ec43cbd44aa1f8163ece1f81.png similarity index 100% rename from content/assets/images/flareon-2017/489d77b797f222ef52533b5da295fd7e733c9156ec43cbd44aa1f8163ece1f81.png rename to static/img/flareon-2017/489d77b797f222ef52533b5da295fd7e733c9156ec43cbd44aa1f8163ece1f81.png diff --git a/content/assets/images/flareon-2017/532e605c764a754f32dbb0d2581913dbf0283d76e21f12cbf92841cfae67f8c4.png b/static/img/flareon-2017/532e605c764a754f32dbb0d2581913dbf0283d76e21f12cbf92841cfae67f8c4.png similarity index 100% rename from content/assets/images/flareon-2017/532e605c764a754f32dbb0d2581913dbf0283d76e21f12cbf92841cfae67f8c4.png rename to static/img/flareon-2017/532e605c764a754f32dbb0d2581913dbf0283d76e21f12cbf92841cfae67f8c4.png diff --git a/content/assets/images/flareon-2017/54e0782509ce641e04edd2b4bb2fef3d80f31c6640451952464ff9d50b5cb851.png b/static/img/flareon-2017/54e0782509ce641e04edd2b4bb2fef3d80f31c6640451952464ff9d50b5cb851.png similarity index 100% rename from content/assets/images/flareon-2017/54e0782509ce641e04edd2b4bb2fef3d80f31c6640451952464ff9d50b5cb851.png rename to static/img/flareon-2017/54e0782509ce641e04edd2b4bb2fef3d80f31c6640451952464ff9d50b5cb851.png diff --git a/content/assets/images/flareon-2017/5afbab3abc4ad96ab713f58c496eaee64e2efb5ae92760a084c6f5cf55a90caa.png b/static/img/flareon-2017/5afbab3abc4ad96ab713f58c496eaee64e2efb5ae92760a084c6f5cf55a90caa.png similarity index 100% rename from content/assets/images/flareon-2017/5afbab3abc4ad96ab713f58c496eaee64e2efb5ae92760a084c6f5cf55a90caa.png rename to static/img/flareon-2017/5afbab3abc4ad96ab713f58c496eaee64e2efb5ae92760a084c6f5cf55a90caa.png diff --git a/content/assets/images/flareon-2017/5b6dc558d6fac2d9e883a67b24934b0ec757855b7fb3736439f6cd0b9585b9a4.png b/static/img/flareon-2017/5b6dc558d6fac2d9e883a67b24934b0ec757855b7fb3736439f6cd0b9585b9a4.png similarity index 100% rename from content/assets/images/flareon-2017/5b6dc558d6fac2d9e883a67b24934b0ec757855b7fb3736439f6cd0b9585b9a4.png rename to static/img/flareon-2017/5b6dc558d6fac2d9e883a67b24934b0ec757855b7fb3736439f6cd0b9585b9a4.png diff --git a/content/assets/images/flareon-2017/638b63ff20d2447bdcf9ca2f7dbf3e9a8800178722580185a0c9c7f86652f707.png b/static/img/flareon-2017/638b63ff20d2447bdcf9ca2f7dbf3e9a8800178722580185a0c9c7f86652f707.png similarity index 100% rename from content/assets/images/flareon-2017/638b63ff20d2447bdcf9ca2f7dbf3e9a8800178722580185a0c9c7f86652f707.png rename to static/img/flareon-2017/638b63ff20d2447bdcf9ca2f7dbf3e9a8800178722580185a0c9c7f86652f707.png diff --git a/content/assets/images/flareon-2017/72de36af7ebcf629992d8b5f9f3a54e20cb01d6335fd961984d34b0840ea4b7e.png b/static/img/flareon-2017/72de36af7ebcf629992d8b5f9f3a54e20cb01d6335fd961984d34b0840ea4b7e.png similarity index 100% rename from content/assets/images/flareon-2017/72de36af7ebcf629992d8b5f9f3a54e20cb01d6335fd961984d34b0840ea4b7e.png rename to static/img/flareon-2017/72de36af7ebcf629992d8b5f9f3a54e20cb01d6335fd961984d34b0840ea4b7e.png diff --git a/content/assets/images/flareon-2017/7d5697ef3325169816f81bd29388f6575c6dd51d23d9fcf11c26dc778f29b354.png b/static/img/flareon-2017/7d5697ef3325169816f81bd29388f6575c6dd51d23d9fcf11c26dc778f29b354.png similarity index 100% rename from content/assets/images/flareon-2017/7d5697ef3325169816f81bd29388f6575c6dd51d23d9fcf11c26dc778f29b354.png rename to static/img/flareon-2017/7d5697ef3325169816f81bd29388f6575c6dd51d23d9fcf11c26dc778f29b354.png diff --git a/content/assets/images/flareon-2017/7ecf50b91265cdd05f48d3910c20c9d48899a3e19645fbe263f8c34a696d00cc.png b/static/img/flareon-2017/7ecf50b91265cdd05f48d3910c20c9d48899a3e19645fbe263f8c34a696d00cc.png similarity index 100% rename from content/assets/images/flareon-2017/7ecf50b91265cdd05f48d3910c20c9d48899a3e19645fbe263f8c34a696d00cc.png rename to static/img/flareon-2017/7ecf50b91265cdd05f48d3910c20c9d48899a3e19645fbe263f8c34a696d00cc.png diff --git a/content/assets/images/flareon-2017/9e3fc6079d951d311ad3bacdee5d98d5d191b63663d7803e93ec1f260cbde521.png b/static/img/flareon-2017/9e3fc6079d951d311ad3bacdee5d98d5d191b63663d7803e93ec1f260cbde521.png similarity index 100% rename from content/assets/images/flareon-2017/9e3fc6079d951d311ad3bacdee5d98d5d191b63663d7803e93ec1f260cbde521.png rename to static/img/flareon-2017/9e3fc6079d951d311ad3bacdee5d98d5d191b63663d7803e93ec1f260cbde521.png diff --git a/content/assets/images/flareon-2017/a06c9b431092bbd3e382f3d703dbe4828d0702f0140ee009aac3b341c145c32e.png b/static/img/flareon-2017/a06c9b431092bbd3e382f3d703dbe4828d0702f0140ee009aac3b341c145c32e.png similarity index 100% rename from content/assets/images/flareon-2017/a06c9b431092bbd3e382f3d703dbe4828d0702f0140ee009aac3b341c145c32e.png rename to static/img/flareon-2017/a06c9b431092bbd3e382f3d703dbe4828d0702f0140ee009aac3b341c145c32e.png diff --git a/content/assets/images/flareon-2017/a0e353204c9ddbd73d9a71c3c6ec53ba7c068d4ab487d43726ebfbe66aef3e8b.png b/static/img/flareon-2017/a0e353204c9ddbd73d9a71c3c6ec53ba7c068d4ab487d43726ebfbe66aef3e8b.png similarity index 100% rename from content/assets/images/flareon-2017/a0e353204c9ddbd73d9a71c3c6ec53ba7c068d4ab487d43726ebfbe66aef3e8b.png rename to static/img/flareon-2017/a0e353204c9ddbd73d9a71c3c6ec53ba7c068d4ab487d43726ebfbe66aef3e8b.png diff --git a/content/assets/images/flareon-2017/a25240fe0264b71f12bf0371e663fe5357dd0b9f6366056b34814a5bd2670e2b.png b/static/img/flareon-2017/a25240fe0264b71f12bf0371e663fe5357dd0b9f6366056b34814a5bd2670e2b.png similarity index 100% rename from content/assets/images/flareon-2017/a25240fe0264b71f12bf0371e663fe5357dd0b9f6366056b34814a5bd2670e2b.png rename to static/img/flareon-2017/a25240fe0264b71f12bf0371e663fe5357dd0b9f6366056b34814a5bd2670e2b.png diff --git a/content/assets/images/flareon-2017/a686c1bd8e99734457a6cec1549cfdb8218e5ebaa9e62e412110bb9a9062508e.png b/static/img/flareon-2017/a686c1bd8e99734457a6cec1549cfdb8218e5ebaa9e62e412110bb9a9062508e.png similarity index 100% rename from content/assets/images/flareon-2017/a686c1bd8e99734457a6cec1549cfdb8218e5ebaa9e62e412110bb9a9062508e.png rename to static/img/flareon-2017/a686c1bd8e99734457a6cec1549cfdb8218e5ebaa9e62e412110bb9a9062508e.png diff --git a/content/assets/images/flareon-2017/ad3a22fa907e8c8185c87b256bfc4fa542c68eb5dfcc508d4ea8620adab9d859.png b/static/img/flareon-2017/ad3a22fa907e8c8185c87b256bfc4fa542c68eb5dfcc508d4ea8620adab9d859.png similarity index 100% rename from content/assets/images/flareon-2017/ad3a22fa907e8c8185c87b256bfc4fa542c68eb5dfcc508d4ea8620adab9d859.png rename to static/img/flareon-2017/ad3a22fa907e8c8185c87b256bfc4fa542c68eb5dfcc508d4ea8620adab9d859.png diff --git a/content/assets/images/flareon-2017/ae6aff7a8232b182109f61df0cd50bf78f7ce4a5f162c8c16a5591b5f0f7aecc.png b/static/img/flareon-2017/ae6aff7a8232b182109f61df0cd50bf78f7ce4a5f162c8c16a5591b5f0f7aecc.png similarity index 100% rename from content/assets/images/flareon-2017/ae6aff7a8232b182109f61df0cd50bf78f7ce4a5f162c8c16a5591b5f0f7aecc.png rename to static/img/flareon-2017/ae6aff7a8232b182109f61df0cd50bf78f7ce4a5f162c8c16a5591b5f0f7aecc.png diff --git a/content/assets/images/flareon-2017/aea95f918e61631fae4e6fe1d003951d1fc30d7fcf0e8ac787b14983e264c876.png b/static/img/flareon-2017/aea95f918e61631fae4e6fe1d003951d1fc30d7fcf0e8ac787b14983e264c876.png similarity index 100% rename from content/assets/images/flareon-2017/aea95f918e61631fae4e6fe1d003951d1fc30d7fcf0e8ac787b14983e264c876.png rename to static/img/flareon-2017/aea95f918e61631fae4e6fe1d003951d1fc30d7fcf0e8ac787b14983e264c876.png diff --git a/content/assets/images/flareon-2017/bbcda00a98ff78d846bfa7a6e2b0e846cdcd50a8cc7cd8b4b4a8b79f4a1b49db.png b/static/img/flareon-2017/bbcda00a98ff78d846bfa7a6e2b0e846cdcd50a8cc7cd8b4b4a8b79f4a1b49db.png similarity index 100% rename from content/assets/images/flareon-2017/bbcda00a98ff78d846bfa7a6e2b0e846cdcd50a8cc7cd8b4b4a8b79f4a1b49db.png rename to static/img/flareon-2017/bbcda00a98ff78d846bfa7a6e2b0e846cdcd50a8cc7cd8b4b4a8b79f4a1b49db.png diff --git a/content/assets/images/flareon-2017/c1042765b377b68599461aa2c7fbabeb502f831a49db09cb5bb6223a22c99bce.png b/static/img/flareon-2017/c1042765b377b68599461aa2c7fbabeb502f831a49db09cb5bb6223a22c99bce.png similarity index 100% rename from content/assets/images/flareon-2017/c1042765b377b68599461aa2c7fbabeb502f831a49db09cb5bb6223a22c99bce.png rename to static/img/flareon-2017/c1042765b377b68599461aa2c7fbabeb502f831a49db09cb5bb6223a22c99bce.png diff --git a/content/assets/images/flareon-2017/c1de5ea5895e4bb38d54167604de4dff8c75dd14d757d40b1d1992419d085232.png b/static/img/flareon-2017/c1de5ea5895e4bb38d54167604de4dff8c75dd14d757d40b1d1992419d085232.png similarity index 100% rename from content/assets/images/flareon-2017/c1de5ea5895e4bb38d54167604de4dff8c75dd14d757d40b1d1992419d085232.png rename to static/img/flareon-2017/c1de5ea5895e4bb38d54167604de4dff8c75dd14d757d40b1d1992419d085232.png diff --git a/content/assets/images/flareon-2017/c2be22c2350ecf3a792cfa07a72ee0c6a55e129e60642577e70994e53c3e2efd.png b/static/img/flareon-2017/c2be22c2350ecf3a792cfa07a72ee0c6a55e129e60642577e70994e53c3e2efd.png similarity index 100% rename from content/assets/images/flareon-2017/c2be22c2350ecf3a792cfa07a72ee0c6a55e129e60642577e70994e53c3e2efd.png rename to static/img/flareon-2017/c2be22c2350ecf3a792cfa07a72ee0c6a55e129e60642577e70994e53c3e2efd.png diff --git a/content/assets/images/flareon-2017/cd4cd292a48fa1fc086b50e5617459edec3e9d40513de244bf57428f0c372348.png b/static/img/flareon-2017/cd4cd292a48fa1fc086b50e5617459edec3e9d40513de244bf57428f0c372348.png similarity index 100% rename from content/assets/images/flareon-2017/cd4cd292a48fa1fc086b50e5617459edec3e9d40513de244bf57428f0c372348.png rename to static/img/flareon-2017/cd4cd292a48fa1fc086b50e5617459edec3e9d40513de244bf57428f0c372348.png diff --git a/content/assets/images/flareon-2017/d9d6b730545915c4d7a94f05ff7b42ab7b5ba9fa5a9bc119147d6a35dd357c18.png b/static/img/flareon-2017/d9d6b730545915c4d7a94f05ff7b42ab7b5ba9fa5a9bc119147d6a35dd357c18.png similarity index 100% rename from content/assets/images/flareon-2017/d9d6b730545915c4d7a94f05ff7b42ab7b5ba9fa5a9bc119147d6a35dd357c18.png rename to static/img/flareon-2017/d9d6b730545915c4d7a94f05ff7b42ab7b5ba9fa5a9bc119147d6a35dd357c18.png diff --git a/content/assets/images/flareon-2017/dc8897ca8ce6dc0a124da94b1e7e7ddf7fc442b137930a003c31875b547c3ec9.png b/static/img/flareon-2017/dc8897ca8ce6dc0a124da94b1e7e7ddf7fc442b137930a003c31875b547c3ec9.png similarity index 100% rename from content/assets/images/flareon-2017/dc8897ca8ce6dc0a124da94b1e7e7ddf7fc442b137930a003c31875b547c3ec9.png rename to static/img/flareon-2017/dc8897ca8ce6dc0a124da94b1e7e7ddf7fc442b137930a003c31875b547c3ec9.png diff --git a/content/assets/images/flareon-2017/dededfb9d354408d37fab58d50b62856c51ef5a3326ab05a42470df936f6dbf1.png b/static/img/flareon-2017/dededfb9d354408d37fab58d50b62856c51ef5a3326ab05a42470df936f6dbf1.png similarity index 100% rename from content/assets/images/flareon-2017/dededfb9d354408d37fab58d50b62856c51ef5a3326ab05a42470df936f6dbf1.png rename to static/img/flareon-2017/dededfb9d354408d37fab58d50b62856c51ef5a3326ab05a42470df936f6dbf1.png diff --git a/content/assets/images/flareon-2017/e87e700ca5ce3926a6f8156fb77f6ea97934cb093ffb2f73e4a89904203fe27c.png b/static/img/flareon-2017/e87e700ca5ce3926a6f8156fb77f6ea97934cb093ffb2f73e4a89904203fe27c.png similarity index 100% rename from content/assets/images/flareon-2017/e87e700ca5ce3926a6f8156fb77f6ea97934cb093ffb2f73e4a89904203fe27c.png rename to static/img/flareon-2017/e87e700ca5ce3926a6f8156fb77f6ea97934cb093ffb2f73e4a89904203fe27c.png diff --git a/content/assets/images/flareon-2017/eaca6198b81df65f296bc6d280437944ee7745fae6c9168d2500b12d0a5c1345.png b/static/img/flareon-2017/eaca6198b81df65f296bc6d280437944ee7745fae6c9168d2500b12d0a5c1345.png similarity index 100% rename from content/assets/images/flareon-2017/eaca6198b81df65f296bc6d280437944ee7745fae6c9168d2500b12d0a5c1345.png rename to static/img/flareon-2017/eaca6198b81df65f296bc6d280437944ee7745fae6c9168d2500b12d0a5c1345.png diff --git a/content/assets/images/flareon-2017/fe5e80d5dd81c1350413732f30ed5ba2b2e4ae1cf92b00504fa6a0bba1b9a820.png b/static/img/flareon-2017/fe5e80d5dd81c1350413732f30ed5ba2b2e4ae1cf92b00504fa6a0bba1b9a820.png similarity index 100% rename from content/assets/images/flareon-2017/fe5e80d5dd81c1350413732f30ed5ba2b2e4ae1cf92b00504fa6a0bba1b9a820.png rename to static/img/flareon-2017/fe5e80d5dd81c1350413732f30ed5ba2b2e4ae1cf92b00504fa6a0bba1b9a820.png diff --git a/content/assets/images/flareon-2018-header.png b/static/img/flareon-2018-header.png similarity index 100% rename from content/assets/images/flareon-2018-header.png rename to static/img/flareon-2018-header.png diff --git a/content/assets/images/flareon-2018/12268605.png b/static/img/flareon-2018/12268605.png similarity index 100% rename from content/assets/images/flareon-2018/12268605.png rename to static/img/flareon-2018/12268605.png diff --git a/content/assets/images/flareon-2018/L5-flag.png b/static/img/flareon-2018/L5-flag.png similarity index 100% rename from content/assets/images/flareon-2018/L5-flag.png rename to static/img/flareon-2018/L5-flag.png diff --git a/content/assets/images/flareon-2018/L6-flag.webm b/static/img/flareon-2018/L6-flag.webm similarity index 100% rename from content/assets/images/flareon-2018/L6-flag.webm rename to static/img/flareon-2018/L6-flag.webm diff --git a/content/assets/images/gef-bh-usa-bg.png b/static/img/gef-bh-usa-bg.png similarity index 100% rename from content/assets/images/gef-bh-usa-bg.png rename to static/img/gef-bh-usa-bg.png diff --git a/content/assets/images/home-bg.jpg b/static/img/home-bg.jpg similarity index 100% rename from content/assets/images/home-bg.jpg rename to static/img/home-bg.jpg diff --git a/content/assets/images/hugsy.png b/static/img/hugsy.png similarity index 100% rename from content/assets/images/hugsy.png rename to static/img/hugsy.png diff --git a/content/assets/images/libfuzzer-lief/fuzz-result.png b/static/img/libfuzzer-lief/fuzz-result.png similarity index 100% rename from content/assets/images/libfuzzer-lief/fuzz-result.png rename to static/img/libfuzzer-lief/fuzz-result.png diff --git a/content/assets/images/libfuzzer-lief/header.png b/static/img/libfuzzer-lief/header.png similarity index 100% rename from content/assets/images/libfuzzer-lief/header.png rename to static/img/libfuzzer-lief/header.png diff --git a/content/assets/images/post-bg-01.jpg b/static/img/post-bg-01.jpg similarity index 100% rename from content/assets/images/post-bg-01.jpg rename to static/img/post-bg-01.jpg diff --git a/content/assets/images/post-bg-02.jpg b/static/img/post-bg-02.jpg similarity index 100% rename from content/assets/images/post-bg-02.jpg rename to static/img/post-bg-02.jpg diff --git a/content/assets/images/post-bg-03.jpg b/static/img/post-bg-03.jpg similarity index 100% rename from content/assets/images/post-bg-03.jpg rename to static/img/post-bg-03.jpg diff --git a/content/assets/images/post-bg-04.jpg b/static/img/post-bg-04.jpg similarity index 100% rename from content/assets/images/post-bg-04.jpg rename to static/img/post-bg-04.jpg diff --git a/content/assets/images/post-bg-05.jpg b/static/img/post-bg-05.jpg similarity index 100% rename from content/assets/images/post-bg-05.jpg rename to static/img/post-bg-05.jpg diff --git a/content/assets/images/post-bg-06.jpg b/static/img/post-bg-06.jpg similarity index 100% rename from content/assets/images/post-bg-06.jpg rename to static/img/post-bg-06.jpg diff --git a/content/assets/images/post-sample-image.jpg b/static/img/post-sample-image.jpg similarity index 100% rename from content/assets/images/post-sample-image.jpg rename to static/img/post-sample-image.jpg diff --git a/content/assets/images/qemu-img.png b/static/img/qemu-img.png similarity index 100% rename from content/assets/images/qemu-img.png rename to static/img/qemu-img.png diff --git a/content/assets/images/quick-visualization/evil.dll.pgm.png b/static/img/quick-visualization/evil.dll.pgm.png similarity index 100% rename from content/assets/images/quick-visualization/evil.dll.pgm.png rename to static/img/quick-visualization/evil.dll.pgm.png diff --git a/content/assets/images/small-pool/dos-1.png b/static/img/small-pool/dos-1.png similarity index 100% rename from content/assets/images/small-pool/dos-1.png rename to static/img/small-pool/dos-1.png diff --git a/content/assets/images/small-pool/ida-setthreaddescription.png b/static/img/small-pool/ida-setthreaddescription.png similarity index 100% rename from content/assets/images/small-pool/ida-setthreaddescription.png rename to static/img/small-pool/ida-setthreaddescription.png diff --git a/content/assets/images/small-pool/ntdiff.png b/static/img/small-pool/ntdiff.png similarity index 100% rename from content/assets/images/small-pool/ntdiff.png rename to static/img/small-pool/ntdiff.png diff --git a/content/assets/images/small-pool/ntsetinformationthread-1.png b/static/img/small-pool/ntsetinformationthread-1.png similarity index 100% rename from content/assets/images/small-pool/ntsetinformationthread-1.png rename to static/img/small-pool/ntsetinformationthread-1.png diff --git a/content/assets/images/small-pool/setthreadname-1.png b/static/img/small-pool/setthreadname-1.png similarity index 100% rename from content/assets/images/small-pool/setthreadname-1.png rename to static/img/small-pool/setthreadname-1.png diff --git a/content/assets/images/small-pool/setthreadname-2.png b/static/img/small-pool/setthreadname-2.png similarity index 100% rename from content/assets/images/small-pool/setthreadname-2.png rename to static/img/small-pool/setthreadname-2.png diff --git a/content/assets/images/tags-bg.png b/static/img/tags-bg.png similarity index 100% rename from content/assets/images/tags-bg.png rename to static/img/tags-bg.png diff --git a/content/assets/images/vbox-to-hyperv-header.png b/static/img/vbox-to-hyperv-header.png similarity index 100% rename from content/assets/images/vbox-to-hyperv-header.png rename to static/img/vbox-to-hyperv-header.png diff --git a/content/assets/images/vbox.png b/static/img/vbox.png similarity index 100% rename from content/assets/images/vbox.png rename to static/img/vbox.png diff --git a/content/assets/images/win-kernel-debug/bg.png b/static/img/win-kernel-debug/bg.png similarity index 100% rename from content/assets/images/win-kernel-debug/bg.png rename to static/img/win-kernel-debug/bg.png diff --git a/content/assets/images/win-kernel-debug/dbg-network-settings.png b/static/img/win-kernel-debug/dbg-network-settings.png similarity index 100% rename from content/assets/images/win-kernel-debug/dbg-network-settings.png rename to static/img/win-kernel-debug/dbg-network-settings.png diff --git a/content/assets/images/win-kernel-debug/dbg-uart-settings.png b/static/img/win-kernel-debug/dbg-uart-settings.png similarity index 100% rename from content/assets/images/win-kernel-debug/dbg-uart-settings.png rename to static/img/win-kernel-debug/dbg-uart-settings.png diff --git a/content/assets/images/win-kernel-debug/hevd-stack-overflow-exploit.png b/static/img/win-kernel-debug/hevd-stack-overflow-exploit.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-stack-overflow-exploit.png rename to static/img/win-kernel-debug/hevd-stack-overflow-exploit.png diff --git a/content/assets/images/win-kernel-debug/hevd-stack-overflow-ida-driver-entry.png b/static/img/win-kernel-debug/hevd-stack-overflow-ida-driver-entry.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-stack-overflow-ida-driver-entry.png rename to static/img/win-kernel-debug/hevd-stack-overflow-ida-driver-entry.png diff --git a/content/assets/images/win-kernel-debug/hevd-stack-overflow-ida-ioctl-1.png b/static/img/win-kernel-debug/hevd-stack-overflow-ida-ioctl-1.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-stack-overflow-ida-ioctl-1.png rename to static/img/win-kernel-debug/hevd-stack-overflow-ida-ioctl-1.png diff --git a/content/assets/images/win-kernel-debug/hevd-stack-overflow-windbg-ret.png b/static/img/win-kernel-debug/hevd-stack-overflow-windbg-ret.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-stack-overflow-windbg-ret.png rename to static/img/win-kernel-debug/hevd-stack-overflow-windbg-ret.png diff --git a/content/assets/images/win-kernel-debug/hevd-www-final-exploit.png b/static/img/win-kernel-debug/hevd-www-final-exploit.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-www-final-exploit.png rename to static/img/win-kernel-debug/hevd-www-final-exploit.png diff --git a/content/assets/images/win-kernel-debug/hevd-www-hal-interrupt.png b/static/img/win-kernel-debug/hevd-www-hal-interrupt.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-www-hal-interrupt.png rename to static/img/win-kernel-debug/hevd-www-hal-interrupt.png diff --git a/content/assets/images/win-kernel-debug/hevd-www-ida-vuln-spotting.png b/static/img/win-kernel-debug/hevd-www-ida-vuln-spotting.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-www-ida-vuln-spotting.png rename to static/img/win-kernel-debug/hevd-www-ida-vuln-spotting.png diff --git a/content/assets/images/win-kernel-debug/hevd-www-testing-exploit.png b/static/img/win-kernel-debug/hevd-www-testing-exploit.png similarity index 100% rename from content/assets/images/win-kernel-debug/hevd-www-testing-exploit.png rename to static/img/win-kernel-debug/hevd-www-testing-exploit.png diff --git a/content/assets/images/win-kernel-debug/register-hevd.png b/static/img/win-kernel-debug/register-hevd.png similarity index 100% rename from content/assets/images/win-kernel-debug/register-hevd.png rename to static/img/win-kernel-debug/register-hevd.png diff --git a/content/assets/images/win-kernel-debug/token-bump-via-windbg-1.png b/static/img/win-kernel-debug/token-bump-via-windbg-1.png similarity index 100% rename from content/assets/images/win-kernel-debug/token-bump-via-windbg-1.png rename to static/img/win-kernel-debug/token-bump-via-windbg-1.png diff --git a/content/assets/images/win-kernel-debug/token-bump-via-windbg-2.png b/static/img/win-kernel-debug/token-bump-via-windbg-2.png similarity index 100% rename from content/assets/images/win-kernel-debug/token-bump-via-windbg-2.png rename to static/img/win-kernel-debug/token-bump-via-windbg-2.png diff --git a/content/assets/images/win-kernel-debug/win7-bcdedit-enable-debug.png b/static/img/win-kernel-debug/win7-bcdedit-enable-debug.png similarity index 100% rename from content/assets/images/win-kernel-debug/win7-bcdedit-enable-debug.png rename to static/img/win-kernel-debug/win7-bcdedit-enable-debug.png diff --git a/content/assets/images/win-kernel-debug/win7-boot-manager.png b/static/img/win-kernel-debug/win7-boot-manager.png similarity index 100% rename from content/assets/images/win-kernel-debug/win7-boot-manager.png rename to static/img/win-kernel-debug/win7-boot-manager.png diff --git a/content/assets/images/win-kernel-debug/win7-debug-session.png b/static/img/win-kernel-debug/win7-debug-session.png similarity index 100% rename from content/assets/images/win-kernel-debug/win7-debug-session.png rename to static/img/win-kernel-debug/win7-debug-session.png diff --git a/content/assets/images/win-kernel-debug/win7-vbox-settings.png b/static/img/win-kernel-debug/win7-vbox-settings.png similarity index 100% rename from content/assets/images/win-kernel-debug/win7-vbox-settings.png rename to static/img/win-kernel-debug/win7-vbox-settings.png diff --git a/content/assets/images/win-kernel-debug/win7-windbg-option.png b/static/img/win-kernel-debug/win7-windbg-option.png similarity index 100% rename from content/assets/images/win-kernel-debug/win7-windbg-option.png rename to static/img/win-kernel-debug/win7-windbg-option.png diff --git a/content/assets/images/win-kernel-debug/win8-boot-loader.png b/static/img/win-kernel-debug/win8-boot-loader.png similarity index 100% rename from content/assets/images/win-kernel-debug/win8-boot-loader.png rename to static/img/win-kernel-debug/win8-boot-loader.png diff --git a/content/assets/images/win-kernel-debug/win8-network-controller-properties.png b/static/img/win-kernel-debug/win8-network-controller-properties.png similarity index 100% rename from content/assets/images/win-kernel-debug/win8-network-controller-properties.png rename to static/img/win-kernel-debug/win8-network-controller-properties.png diff --git a/content/assets/images/win-kernel-debug/win8-setup-kernel-mode.png b/static/img/win-kernel-debug/win8-setup-kernel-mode.png similarity index 100% rename from content/assets/images/win-kernel-debug/win8-setup-kernel-mode.png rename to static/img/win-kernel-debug/win8-setup-kernel-mode.png diff --git a/content/assets/images/win-kernel-debug/win8-success.png b/static/img/win-kernel-debug/win8-success.png similarity index 100% rename from content/assets/images/win-kernel-debug/win8-success.png rename to static/img/win-kernel-debug/win8-success.png diff --git a/content/assets/images/windbg-ttd/header.png b/static/img/windbg-ttd/header.png similarity index 100% rename from content/assets/images/windbg-ttd/header.png rename to static/img/windbg-ttd/header.png diff --git a/content/assets/images/windbg-ttd/notepad1.png b/static/img/windbg-ttd/notepad1.png similarity index 100% rename from content/assets/images/windbg-ttd/notepad1.png rename to static/img/windbg-ttd/notepad1.png diff --git a/content/assets/images/windbg-ttd/startrecord.PNG b/static/img/windbg-ttd/startrecord.PNG similarity index 100% rename from content/assets/images/windbg-ttd/startrecord.PNG rename to static/img/windbg-ttd/startrecord.PNG diff --git a/content/assets/images/{1910FC37-E777-418F-83EC-2A2543969515}.jpg b/static/img/{1910FC37-E777-418F-83EC-2A2543969515}.jpg similarity index 100% rename from content/assets/images/{1910FC37-E777-418F-83EC-2A2543969515}.jpg rename to static/img/{1910FC37-E777-418F-83EC-2A2543969515}.jpg diff --git a/content/assets/images/{68EB5886-B508-4F69-81E2-DDC726638542}.png b/static/img/{68EB5886-B508-4F69-81E2-DDC726638542}.png similarity index 100% rename from content/assets/images/{68EB5886-B508-4F69-81E2-DDC726638542}.png rename to static/img/{68EB5886-B508-4F69-81E2-DDC726638542}.png diff --git a/content/assets/images/{D1BF677A-5CFD-4C16-8ABA-1492397D7E17}.jpg b/static/img/{D1BF677A-5CFD-4C16-8ABA-1492397D7E17}.jpg similarity index 100% rename from content/assets/images/{D1BF677A-5CFD-4C16-8ABA-1492397D7E17}.jpg rename to static/img/{D1BF677A-5CFD-4C16-8ABA-1492397D7E17}.jpg diff --git a/templates/about.html b/templates/about.html new file mode 100644 index 00000000..8eb7df0c --- /dev/null +++ b/templates/about.html @@ -0,0 +1,30 @@ +{% extends "index.html" %} + +{% block header %} + +
+
+
+
+
+
+
+

About Me

+ This is what I do. +
+
+
+
+
+{% endblock header %} + +{% block content %} + +
+
+
+ {{ page.content | safe }} +
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/author.html b/templates/author.html new file mode 100644 index 00000000..c6e2099d --- /dev/null +++ b/templates/author.html @@ -0,0 +1,97 @@ +{% import "macros/page.html" as page_macros %} + +{% extends "base.html" %} + +{% block header %} + +
+
+
+
+
+
+ {{ page_macros::page_header(page=page) }} +
+
+
+
+
+{% endblock header %} + +{% block content %} + +
+
+
+
+
+ + +
    + {% if page.extra.github %} +
  • + + + + + + +
  • + {% endif %} + + {% if page.extra.twitter %} +
  • + + + + + + +
  • + {% endif %} + + {% if page.extra.discord %} +
  • + + + + + + +
  • + {% endif %} +
+ +
+ + + {{ page.content | safe }} + +
+ + + {# Get pages by authors #} + {% set section = get_section(path="_index.md") %} + {% set_global pages_by_author = [] %} + {% for p in section.pages %} + {% if p.authors is containing(page.title) %} + {% set_global pages_by_author = pages_by_author | concat(with=p) %} + {% endif %} + {% endfor %} + + Articles by {{page.title}} ({{ pages_by_author | length }}): +
    + {% for p in pages_by_author %} +
  • + {{ p.date | date(format="%Y/%m/%d") }} - {{ p.title }} +
  • + {% endfor %} +
+
+
+
+
+{% endblock content %} + +{% block extrascripts %} +{% endblock extrasripts %} diff --git a/templates/authors.html b/templates/authors.html new file mode 100644 index 00000000..90912a31 --- /dev/null +++ b/templates/authors.html @@ -0,0 +1,23 @@ +{% import "macros/page.html" as page_macros %} + +{% extends "base.html" %} + +{% block content %} + +
+
+
+
+ Authors: + +
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 00000000..8b45535e --- /dev/null +++ b/templates/base.html @@ -0,0 +1,128 @@ + + + + + + + + + + {% block header_meta %} + {% endblock header_meta %} + + {% block analytics %} + {% endblock analytics %} + + {% block title %}{{ config.title }}{% endblock title %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block header %} +
+
+
+
+
+
+
+

{{config.title}}

+ {{config.description}} +
+
+
+
+
+ {% endblock header %} + + {% block content %} + {% endblock content %} + +
+ + +
+
+
+
+
    + {% for item in config.extra.clean_blog_social %} +
  • + + + + + + +
  • + {% endfor %} +
+ +
+
+
+
+ + + + + + + + + + {% block extrascripts %} + {% endblock extrasripts %} + + + \ No newline at end of file diff --git a/templates/categories/list.html b/templates/categories/list.html new file mode 100644 index 00000000..0a36347d --- /dev/null +++ b/templates/categories/list.html @@ -0,0 +1,17 @@ +{% extends "index.html" %} + +{% block title %}{{ config.title }} - Categories{% endblock %} + +{% block content %} +
+
+
+ +
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/categories/single.html b/templates/categories/single.html new file mode 100644 index 00000000..531d277a --- /dev/null +++ b/templates/categories/single.html @@ -0,0 +1,3 @@ +{% extends "index.html" %} + +{% block title %}{{ config.title }} - Articles in the {{ term.name }} category{% endblock %} \ No newline at end of file diff --git a/templates/contact.html b/templates/contact.html new file mode 100644 index 00000000..43a5a8c1 --- /dev/null +++ b/templates/contact.html @@ -0,0 +1,61 @@ +{% extends "base.html" %} + +{% block header %} + +
+
+
+
+
+
+

Contact Me

+ Have questions? I have answers. +
+
+
+
+
+{% endblock header %} + +{% block content %} + +
+
+
+

Want to get in touch? Fill out the form below to send me a message and I will get back to you as soon as + possible!

+
+
+
+ + +

+
+
+
+
+ + +

+
+
+
+
+ + +

+
+
+
+
+
+ +
+
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 00000000..d92bcbff --- /dev/null +++ b/templates/index.html @@ -0,0 +1,70 @@ +{% import "macros/page.html" as page_macros %} + +{% extends "base.html" %} + +{% block header_meta %} + + + + + + + + + + + + + + + + + + + + + +{% endblock header_meta %} + +{% block content %} + +
+
+
+
+ {% for page in paginator.pages %} +
+ {{ page_macros::page_title(page=page) }} + {% if page.summary %} +

{{ page.summary | safe }}

+ {% else %} +

{{ page.content | safe | striptags | truncate(length=200) }}

+ {% endif %} +
+ {% endfor %} +
+ + +
+ {% if paginator.previous %} + ← Newer Posts + {% endif %} + {% if paginator.next %} + Older Posts → + {% endif %} +
+ +
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/macros/page.html b/templates/macros/page.html new file mode 100644 index 00000000..fcc85605 --- /dev/null +++ b/templates/macros/page.html @@ -0,0 +1,32 @@ +{% macro page_title(page) %} + +

{{ page.title }}

+
+

+ Published by {{page.authors | join(sep=", ")}} + {% if page.date %} + , on {{ page.date | date(format="%e %B %Y")}} + {% endif %} + {% if page.taxonomies.categories %} + , under {{ page.taxonomies.categories | join(sep=", ") }} + {% endif %} +

+{% endmacro page_title %} + +{% macro page_header(page) %} +

{{ page.title }}

+ + + • + {% for author in page.authors %} + {{author}} • + {% endfor %} + + {% if page.date %} + {{ page.date | date(format="%e %B %Y")}} + {% endif %} +
+
+ Reading time: {{ page.reading_time }} min +
+{% endmacro page_header %} diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 00000000..347f1262 --- /dev/null +++ b/templates/page.html @@ -0,0 +1,183 @@ +{% import "macros/page.html" as page_macros %} + +{% extends "base.html" %} + +{% block title %} + {{ page.title }} +{% endblock title %} + +{% block header_meta %} + + + + + + + + + + + + + + + + + + +{% endblock header_meta %} + +{% block header %} + +
+
+
+
+
+
+ {{ page_macros::page_header(page=page) }} +
+
+
+
+
+{% endblock header %} + +{% block content %} + +
+
+
+
+
+ {{ page.content | safe }} + + + + {% if page.extra.no_discuss and page.extra.no_discuss == true %} + {% else %} + + {% endif %} + + +
+
+
+
+{% endblock content %} + +{% block extrascripts %} + +
+{% endblock extrasripts %} \ No newline at end of file diff --git a/templates/shortcodes/abbr.html b/templates/shortcodes/abbr.html new file mode 100644 index 00000000..51e2f44c --- /dev/null +++ b/templates/shortcodes/abbr.html @@ -0,0 +1 @@ +{{abbr}} \ No newline at end of file diff --git a/templates/shortcodes/github.html b/templates/shortcodes/github.html new file mode 100644 index 00000000..df97927c --- /dev/null +++ b/templates/shortcodes/github.html @@ -0,0 +1,3 @@ + +{{user}} + diff --git a/templates/shortcodes/img.html b/templates/shortcodes/img.html new file mode 100644 index 00000000..f1abbdbd --- /dev/null +++ b/templates/shortcodes/img.html @@ -0,0 +1,24 @@ + +{{ title }} + \ No newline at end of file diff --git a/templates/shortcodes/note.html b/templates/shortcodes/note.html new file mode 100644 index 00000000..d3bb80cf --- /dev/null +++ b/templates/shortcodes/note.html @@ -0,0 +1,36 @@ +
+ {% if type and type == "caution" %} + + Caution + {% elif type and type == "warning" %} + + Warning + {% elif type and type == "important" %} + + Important + {% elif type and type == "tip" %} + + Tip + {% else %} + + Note + {% endif %} + +

{{ body | markdown | safe }} +

+
diff --git a/templates/shortcodes/twitter.html b/templates/shortcodes/twitter.html new file mode 100644 index 00000000..f2542905 --- /dev/null +++ b/templates/shortcodes/twitter.html @@ -0,0 +1,3 @@ + +@{{user}} + diff --git a/templates/shortcodes/youtube.html b/templates/shortcodes/youtube.html new file mode 100644 index 00000000..314d656c --- /dev/null +++ b/templates/shortcodes/youtube.html @@ -0,0 +1,3 @@ + +@{{title}} + diff --git a/templates/tags/list.html b/templates/tags/list.html new file mode 100644 index 00000000..3b5914d9 --- /dev/null +++ b/templates/tags/list.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block title %}{{ config.title }} - Tags{% endblock %} + +{% block content %} +
+
+
+ +
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/tags/single.html b/templates/tags/single.html new file mode 100644 index 00000000..6e0f0544 --- /dev/null +++ b/templates/tags/single.html @@ -0,0 +1,3 @@ +{% extends "base.html" %} + +{% block title %}{{ config.title }} - Articles in the {{ term.name }} tag{% endblock %} \ No newline at end of file diff --git a/themes/zola-clean-blog b/themes/zola-clean-blog new file mode 160000 index 00000000..e145d57a --- /dev/null +++ b/themes/zola-clean-blog @@ -0,0 +1 @@ +Subproject commit e145d57ace08aa69a9be52b6898fbe531139ab5d