Sothe problem is that dependencies of pwntools are not available on windows from any configured channels. In this specific case, you are unfortunately out of luck installing from conda, as don't know of any channel that provides ncurses on windows.
This is the command I'm using: python3 -m pip install --upgrade pwnwithout the --upgrade part I still get the same error message.If I replace pwn with pwntools I still get the same error message as well.
Edit:OK, so, on closer inspection, it appears that pwntools is looking for python 3.8 setuptools and, on not finding it because I have 3.9 on this machine, tries to download it and gets blocked by the corporate firewall. And no, I don't have access to 3.8 on this machine, so I've hit a brick wall and can go no further.
pip is trying to get the setuptools from internet. And it failed, probably because you don't have internet.
I had the same log if I try to install pwntools without setuptools. And if you give pip -v you will be able to get the full command that has failed.
A few weeks ago i just started with binary exploitation and as learning and understanding this topic is not enough challenging, i encountered different problems with the tools and some basics. One of these problems i will describe today.
there are many good tutorials, challanges and ctfs out there, where you can start learning the topic. After some reverse engineering lessons and understanding the basics i decided to do the ropemporium series, also because CryptoCat has a good youtube series about it, so you can watch them if you get stuck or want to proof if there are other solutions.
first i decided to concentrate on the 64bit binaries (ropemporium always provide each challenge in different architectures). Completed the first challenge without any problem. The second challenge (split) is about providing a correct parameter to your Gadget. I remembered from LiveOverflow binexp. series that there is a difference between how 32bit and 64bit binaries handle function arguments (stack vs. registers). So i short decided to do the 32bit challenges as well to get a better understanding and practice about that.
The interesting thing is, if you start the binary with in gdb standalone gdb ./split32 everything is fine and you can debug through the hole binary without any crash. So i thought it must have been related to pwntools. I googled a lot about the missing cache file (cacheinfo.c) gdb is claiming about but cant found any one else having this problem.
Make sure you have the latest gdbserver version installed. This makes sense because with gdb.debug pwntools using the gdbserver and not gdb itself, so this is also the reason why debugging in gdb standalone was always working.
The new python 3.11 might scream regarding creating virtual environment and directly copy pasting these instructions might not work.You might pass --break-system-packages with pip install *Not recommended.Or you might use apt install python3- to install python packages system-wide. Do your own research.
Okay so setting our context, or connecting to the remote server or attaching to a local process should be done at this moment and we are good to go now. Setting up context.binary or creating ELF object will by default print the checksec results .(Various protections applicable to this binary).
If we are getting the address leaked as byte code, we need to pad it to get 8 bytes and then unpack it to convert it to interger as we already know that p64() accepts non strings(hex, int) and not bytecode. E.g. puts_address = u64(output[0].ljust(8, b"\x00"))
Sometimes the leaked address is in ASCII format. We need to retrieve flag from the leaked address. So converting the ASCII value to string is important. E.g. flag += bytes.fromhex(word.decode("utf-8"))[::-1].decode("utf-8")
Another great utility pwntools offer is getting functions from different sections of a binary. The symbol table contains information about the symbols defined in the binary object file, such as function and variable names, their types, and their addresses. For example:
In basic exploitations binary.symbols. will come handy. And in advanced exploits binary.got., binary.plt. comes in handy. Remember while adding the addresses to the payload we need to pack it. E.g. p64(binary.symbols.puts)
We can debug our exploit in multiple ways. One easy way is to set log_level.
context.log_level = "debug"When we set our log level to debug we can check the bytes we are sending and receiving over the network.
Normally we use ROPgadget to find the gadgets within the binary.But pwntools also provides way to create ROPChain. Using the ROP object we will be able to discover different gadgets from the binary. find_gadget() returns a list where the first element is the address of the gadget.
As always, you can follow along by using my VM which has all of the tools you'll need to solve this challenge. You'll find the binary in /home/kali/reverse_engineering/pwn/batman. If you don't want to use my VM, you'll need to download the binary here.
My Python code is written in Python3, so you'll need Python3 in order to run my code. You'll also need to install pwntools as I will use it to make exploitation a little easier. Pwntools is a great exploitation framework and I would advise you to play around with it when you have time! You can install pwntools with:
Finally, you'll need a disassembler. I recommend IDA or Ghidra. I'll be using Ghidra throughout this tutorial. If you're using my VM, ghidraRun and ida64, are in the path so you can execute them from anywhere. Alright with all of that out of the way, let's get started!
Alright, we see it is a 64-bit binary, it is dynamically linked, and unfortunately, the symbols have been stripped from the binary. Now, the fact that this binary is 64-bit is pretty important because it will change how we exploit the binary. If you've ever done a buffer overflow on a 32-bit binary you'll see the difference. If not, that's ok, I'll be sure to point them out. They aren't huge differences but they are differences nonetheless. Since we don't have symbols we can only inspect this binary's dynamic symbols with nm -D. Let's go ahead and take a look at the dynamic symbols.
We don't see anything too interesting. However, we do see the read function being used. This function takes user input but, unlike other functions like scanf, it does not stop when a NULL character has been reached. This is good because it'll make developing our exploit a little easier. Let's take a look at the strings.
Alright, well it looks like we found the "secret" password! That was pretty easy! I am a little disappointed in Batman for having such a poor password. You'd think The Dark Knight would know better. Anyway, we see that there are two options: "1. Track Joker", and "2. Chase Joker." We also see the binary prints the address of Joker using the %p format specifier. We aren't entirely sure what variable this address belongs to, but if you've ever done a buffer overflow you might already know. We also see a format specifier of %15s. This is likely the format specifier for grabbing the password. Since this is hardcoded it is likely that there is not a buffer overflow in the password field. Before we open this up in Ghidra, let's run checksec so we can see what protections this binary employs to prevent us from attacking it.
And this binary is completely wide open lol. No stack canaries and the stack is executable. If you're unfamiliar with these concepts I discuss the output in greater detail in the Jeeves post. We also this binary has segments marked as read, write, and execute. This is likely the stack, but we can confirm this. We will use readelf for this task. The following command will print out the segments for this binary:
The GNU_STACK header simply tells the system how to handle the stack when it is loaded, so you won't see an offset, virtual address, or physical address. But, as you can see, the stack has the read, write, and executable flags set. This is great news because we don't have to worry about bypassing any of those pesky protections! This means we can place executable code on the stack and if we can get the instruction pointer to point to our code, the CPU will happily execute it. If that sounds bad, that's because it is! Alright, we now have a decent amount of information now we just need to figure out what buffer we will use to mount our attack. Let's open this up in Ghidra.
Right off the bat we see this binary calls another function, FUN_001011A9. I won't go into the disassembly for this function. This function doesn't do anything but call setvbuf on stdin and stdout. Not very interesting if you ask me. This function is equivalent to the following C code:
We see that a memory address, RBP-0x60 gets loaded in the RAX register and then incremented by 4. This is a little confusing but this is essentially grabbing a stack variable located at RBP-0x5C. We then see a 0x10 and 0x0 get loaded into EDX and ESI respectively. Finally, our stack variable is placed in the RDI register before memset gets called. This tells us that RBP-0x5C is some type of string that is 16 bytes long. Before it gets used, 16 zeroes are stored at that memory address. We then see the prompt is printed. Next, we see RBP-0x60 is loaded into the RAX register, but this time, we don't see an add instruction. That means there is another stack variable located at RBP-0x60. This variable gets loaded into the RSI register which is used as an argument to the call to scanf. We can see what the format specifier is by double clicking on DAT_00102069.
We see the format specifier for the scanf function call is %d so we know that RBP-0x60 is an integer. We also know this represents the choice we have to make, "1: Track Joker" or "2: Chase Joker." Unfortunately, we cannot give meaningful names to these stack variables because, as you can see, Ghidra only sees one stack variable local_68. We'll have to keep track of these ourselves. In our own notepad, let's name RBP-0x5C to buffer and RBP-0x60 to choice. After it grabs our user input it compares it to 1. Before we move on let's write the C code:
3a8082e126