Binary Exploitation with Python3

I have been working on some of the ‘easy’ pwn challenges and I keep running into issues with python3 and properly encoding my payload (specifically with using pwn-tools). What worked in Python2 isn’t working in Python3 and I have read up on how Python3 handles String and Bytes differently from Python2. I find that most write-ups/guides using pwn-tools or doing binary exploitation in general seem to use python2.

Whats got me caught up is that Kali (2021.1) appears to no longer have python-pip (python2) and only supports pip3 which means I cannot install pwn-tools for python2. So I figured I’d try working with Python3… but I can’t figure out the proper way to encode the payload and this seems to be affecting my ability to successfully exploit multiple challenges.

I have tried converting to bytes object, encoding, but cant find the solution to get the desired output I was getting with Python2.

Example (python2):
python -c "print 'A'*5 + '\xde\xad\xc0\xde'" | hexdump -C 41 41 41 41 41 de ad c0 de 0a

Example (python3):
python3 -c "print('A'*5 + '\xde\xad\xc0\xde')" | hexdump -C 41 41 41 41 41 c3 9e c2 ad c3 80 c3 9e 0a

If anyone has any tips on how to properly encode a payload so get the desired output I’d greatly appreciate it!

It’s a tad bit clunky, but you need to use the stdout buffer:

python3 -c "import sys; sys.stdout.buffer.write(b'A'*5 + b'\xde\xad\xc0\xde')" | xxd
00000000: 4141 4141 41de adc0 de                   AAAAA....

When using pwntools, you usually don’t need to care about the encoding back and forth, though. The tubes accept byte strings, by default. That way, you can (transparently) send binary data to a process’ stdin, a network socket, or whatever you need to communicate with.
E.g. for locally testing/developing an exploit, you can use something like:

from pwn import *

REMOTE=False      # switch to True, once your exploit works locally
if REMOTE:
  p = remote('vulnservice.web', 1234)
else:
  p = process('./vuln-binary')

banner = p.read(1024)
buffer  = b'A'*5    # padding to reach overflow
buffer += b'\xde\xad\xc0\xde' # return address of JMP ESP in glibc
buffer += fancy_shellcode
p.send(buffer)
...

@HomeSen thanks for the response and the tip on using stdout buffer. I may have been over complicating it so thank you for the example!