From fa6397c9208b6840003be1f10a8409f81ff118f3 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Thu, 3 Apr 2025 00:36:39 +0200 Subject: [PATCH 1/3] Drop newline in `tube.recvline` by default Change the default value of the `drop` parameter to `True` to avoid the common `recvline().strip()` dance and be in line with other readline implementations. --- docs/source/intro.rst | 6 ++-- pwnlib/adb/adb.py | 2 +- pwnlib/asm.py | 2 +- pwnlib/elf/corefile.py | 2 +- pwnlib/encoders/amd64/delta.py | 2 +- pwnlib/encoders/arm/xor.py | 2 +- pwnlib/encoders/i386/xor.py | 2 +- pwnlib/encoders/mips/xor.py | 2 +- pwnlib/filesystem/ssh.py | 2 +- pwnlib/gdb.py | 36 +++++++++---------- pwnlib/rop/ret2dlresolve.py | 4 +-- pwnlib/rop/rop.py | 2 +- pwnlib/rop/srop.py | 10 +++--- .../templates/aarch64/darwin/cat.asm | 2 +- .../templates/aarch64/darwin/cat2.asm | 2 +- .../templates/aarch64/linux/cat.asm | 2 +- .../templates/aarch64/linux/cat2.asm | 2 +- .../templates/aarch64/linux/echo.asm | 2 +- .../templates/aarch64/linux/stage.asm | 2 +- .../templates/amd64/linux/stage.asm | 2 +- pwnlib/shellcraft/templates/amd64/strcpy.asm | 2 +- pwnlib/shellcraft/templates/arm/linux/cat.asm | 2 +- .../shellcraft/templates/arm/linux/cat2.asm | 2 +- .../shellcraft/templates/arm/linux/echo.asm | 2 +- .../shellcraft/templates/i386/linux/stage.asm | 2 +- pwnlib/shellcraft/templates/i386/strcpy.asm | 2 +- .../shellcraft/templates/thumb/linux/cat.asm | 2 +- .../shellcraft/templates/thumb/linux/cat2.asm | 2 +- .../shellcraft/templates/thumb/linux/echo.asm | 2 +- .../templates/thumb/linux/stage.asm | 2 +- pwnlib/tubes/listen.py | 6 ++-- pwnlib/tubes/process.py | 16 ++++----- pwnlib/tubes/server.py | 4 +-- pwnlib/tubes/ssh.py | 24 ++++++------- pwnlib/tubes/tube.py | 26 ++++++++------ pwnlib/util/misc.py | 12 +++---- 36 files changed, 100 insertions(+), 96 deletions(-) diff --git a/docs/source/intro.rst b/docs/source/intro.rst index 3832dde32..f8eefe331 100644 --- a/docs/source/intro.rst +++ b/docs/source/intro.rst @@ -43,7 +43,7 @@ For example, remote connections via :mod:`pwnlib.tubes.remote`. >>> conn.send(b'USER anonymous\r\n') >>> conn.recvuntil(b' ', drop=True) b'331' - >>> conn.recvline() + >>> conn.recvline(drop=False) b'Please specify the password.\r\n' >>> conn.close() @@ -65,7 +65,7 @@ Interacting with processes is easy thanks to :mod:`pwnlib.tubes.process`. >>> sh.recvline(timeout=1) b'' >>> sh.recvline(timeout=5) - b'hello world\n' + b'hello world' >>> sh.close() Not only can you interact with processes programmatically, but you can @@ -91,7 +91,7 @@ a ``process`` tube. >>> sh.recvline(timeout=1) b'' >>> sh.recvline(timeout=5) - b'hello world\n' + b'hello world' >>> shell.close() Packing Integers diff --git a/pwnlib/adb/adb.py b/pwnlib/adb/adb.py index e2d8ebce7..722a8ec16 100644 --- a/pwnlib/adb/adb.py +++ b/pwnlib/adb/adb.py @@ -322,7 +322,7 @@ def __do_deferred_initialization(self): r.recvuntil('OK') r.recvline() # Rest of the line r.sendline('avd name') - self.avd = r.recvline().strip() + self.avd = r.recvline() except: pass diff --git a/pwnlib/asm.py b/pwnlib/asm.py index 8716e8976..1dc38fe2d 100644 --- a/pwnlib/asm.py +++ b/pwnlib/asm.py @@ -608,7 +608,7 @@ def make_elf(data, >>> p = process(filename) >>> p.sendline(b'echo Hello; exit') >>> p.recvline() - b'Hello\n' + b'Hello' """ retval = None diff --git a/pwnlib/elf/corefile.py b/pwnlib/elf/corefile.py index 16b486657..2d15a3c43 100644 --- a/pwnlib/elf/corefile.py +++ b/pwnlib/elf/corefile.py @@ -361,7 +361,7 @@ class Corefile(ELF): >>> io = process(elf.path, env=env) >>> io.sendline(b'echo hello') >>> io.recvline() - b'hello\n' + b'hello' The process is still running, but accessing its :attr:`.process.corefile` property automatically invokes GDB to attach and dump a corefile. diff --git a/pwnlib/encoders/amd64/delta.py b/pwnlib/encoders/amd64/delta.py index 51482ff3e..f62cea47e 100644 --- a/pwnlib/encoders/amd64/delta.py +++ b/pwnlib/encoders/amd64/delta.py @@ -18,7 +18,7 @@ class amd64DeltaEncoder(i386DeltaEncoder): >>> p = run_shellcode(encoded) >>> p.sendline(b'echo hello; exit') >>> p.recvline() - b'hello\n' + b'hello' """ assembly = ''' base: diff --git a/pwnlib/encoders/arm/xor.py b/pwnlib/encoders/arm/xor.py index 331d06133..91fddc260 100644 --- a/pwnlib/encoders/arm/xor.py +++ b/pwnlib/encoders/arm/xor.py @@ -21,7 +21,7 @@ class ArmXorEncoder(Encoder): >>> p = run_shellcode(encoded) >>> p.sendline(b'echo hello; exit') >>> p.recvline() - b'hello\n' + b'hello' """ arch = 'arm' diff --git a/pwnlib/encoders/i386/xor.py b/pwnlib/encoders/i386/xor.py index e28b00db7..4274db15c 100644 --- a/pwnlib/encoders/i386/xor.py +++ b/pwnlib/encoders/i386/xor.py @@ -33,7 +33,7 @@ class i386XorEncoder(Encoder): >>> p = run_shellcode(encoded) >>> p.sendline(b'echo hello; exit') >>> p.recvline() - b'hello\n' + b'hello' >>> encoders.i386.xor.encode(asm(shellcraft.execve('/bin/sh')), avoid=bytearray([0x31])) b'\xd9\xd0\xd9t$\xf4^\xfcj\x07Y\x83\xc6\x19\x89\xf7\xad\x93\xad1\xd8\xabIu\xf7\x00\x00\x00\x00h\x01\x01\x01\x00\x00\x00\x00\x01\x814$\x00\x00\x00\x00.ri\x01\x00\x00\x00\x00h/bi\x00\x00\x00\x01n\x89\xe30\x00\x01\x00\x00\xc90\xd2j\x00\x00\x00\x00\x0bX\xcd\x80' """ diff --git a/pwnlib/encoders/mips/xor.py b/pwnlib/encoders/mips/xor.py index 9bb4bf6f0..474afb60a 100644 --- a/pwnlib/encoders/mips/xor.py +++ b/pwnlib/encoders/mips/xor.py @@ -109,7 +109,7 @@ class MipsXorEncoder(Encoder): >>> p = run_shellcode(encoded) >>> p.sendline(b'echo hello; exit') >>> p.recvline() - b'hello\n' + b'hello' """ arch = 'mips' diff --git a/pwnlib/filesystem/ssh.py b/pwnlib/filesystem/ssh.py index 49a91d0fd..ce15e3670 100644 --- a/pwnlib/filesystem/ssh.py +++ b/pwnlib/filesystem/ssh.py @@ -500,7 +500,7 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=True): >>> f = SSHPath('dirA/dirB/dirC', ssh=ssh_conn) >>> f.mkdir(parents=True) >>> ssh_conn.run(['ls', '-la', f.absolute().path], env={'LC_ALL': 'C.UTF-8'}).recvline() - b'total 8\n' + b'total 8' """ if exist_ok and self.is_dir(): return diff --git a/pwnlib/gdb.py b/pwnlib/gdb.py index d35920c7b..1c1cfd97a 100644 --- a/pwnlib/gdb.py +++ b/pwnlib/gdb.py @@ -194,7 +194,7 @@ def debug_assembly(asm, gdbscript=None, vma=None, api=False): >>> assembly = shellcraft.echo("Hello world!\n") >>> io = gdb.debug_assembly(assembly) >>> io.recvline() - b'Hello world!\n' + b'Hello world!' """ tmp_elf = make_elf_from_assembly(asm, vma=vma, extract=False) os.chmod(tmp_elf, 0o777) @@ -229,7 +229,7 @@ def debug_shellcode(data, gdbscript=None, vma=None, api=False): >>> shellcode = asm(assembly) >>> io = gdb.debug_shellcode(shellcode) >>> io.recvline() - b'Hello world!\n' + b'Hello world!' """ if isinstance(data, str): log.error("Shellcode is cannot be unicode. Did you mean debug_assembly?") @@ -489,7 +489,7 @@ def debug(args, gdbscript=None, gdb_args=None, exe=None, ssh=None, env=None, por >>> io.sendline(b"echo hello") >>> io.recvline() - b'hello\n' + b'hello' Interact with the process @@ -513,7 +513,7 @@ def debug(args, gdbscript=None, gdb_args=None, exe=None, ssh=None, env=None, por >>> io.sendline(b"echo hello") >>> io.recvline() - b'hello\n' + b'hello' Interact with the process @@ -525,7 +525,7 @@ def debug(args, gdbscript=None, gdb_args=None, exe=None, ssh=None, env=None, por >>> io = gdb.debug(args=[b'\xde\xad\xbe\xef'], gdbscript='continue', exe="/bin/sh") >>> io.sendline(b"echo $0") >>> io.recvline() - b'\xde\xad\xbe\xef\n' + b'\xde\xad\xbe\xef' >>> io.close() Demonstrate that LD_PRELOAD is respected @@ -571,7 +571,7 @@ def debug(args, gdbscript=None, gdb_args=None, exe=None, ssh=None, env=None, por >>> io = gdb.debug(args=[b'\xde\xad\xbe\xef'], gdbscript='continue', exe="/bin/sh", ssh=shell) >>> io.sendline(b"echo $0") >>> io.recvline() - b'$ \xde\xad\xbe\xef\n' + b'$ \xde\xad\xbe\xef' >>> io.close() Using an empty args[0] on a remote process @@ -579,7 +579,7 @@ def debug(args, gdbscript=None, gdb_args=None, exe=None, ssh=None, env=None, por >>> io = gdb.debug(args=[], gdbscript='continue', exe="/bin/sh", ssh=shell) >>> io.sendline(b"echo $0") >>> io.recvline() - b'$ \n' + b'$ ' >>> io.close() @@ -616,12 +616,12 @@ def debug(args, gdbscript=None, gdb_args=None, exe=None, ssh=None, env=None, por >>> io.gdb.continue_nowait() >>> io.recvline() - b'foo\n' + b'foo' >>> io.close() >>> ssh_io.gdb.continue_nowait() >>> ssh_io.recvline() - b'foo\n' + b'foo' >>> ssh_io.close() >>> shell.close() """ @@ -983,7 +983,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr ... quit ... ''') >>> io.recvline() - b'Hello from process debugger!\n' + b'Hello from process debugger!' >>> io.sendline(b'echo Hello from bash && exit') >>> io.recvall() b'Hello from bash\n' @@ -1007,7 +1007,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr Observe the forced line >>> io.recvline() - b'Hello from process debugger!\n' + b'Hello from process debugger!' Interact with the program in a regular way @@ -1031,7 +1031,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr ... quit ... ''') >>> io.recvline() - b'Hello from remote debugger!\n' + b'Hello from remote debugger!' >>> io.sendline(b'echo Hello from bash && exit') >>> io.recvall() b'Hello from bash\n' @@ -1052,12 +1052,12 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr ... end ... ''') >>> gdbserver.recvline(timeout=10) # doctest: +ELLIPSIS - b'Remote debugging from host 127.0.0.1, ...\n' + b'Remote debugging from host 127.0.0.1, ...' >>> gdbserver.recvline(timeout=10) - b'Hello from gdbserver debugger!\n' + b'Hello from gdbserver debugger!' >>> gdbserver.sendline(b'echo Hello from bash && exit') >>> gdbserver.recvline(timeout=10) - b'Hello from bash\n' + b'Hello from bash' >>> gdbserver.close() Attach to processes running on a remote machine via an SSH :class:`.ssh` process @@ -1071,10 +1071,10 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr ... quit ... ''') >>> io.recvline(timeout=5) # doctest: +SKIP - b'Hello from ssh debugger!\n' + b'Hello from ssh debugger!' >>> io.sendline(b'This will be echoed back') >>> io.recvline() - b'This will be echoed back\n' + b'This will be echoed back' >>> io.close() To attach to remote gdbserver, assume you have a socat server delivering gdbserver @@ -1095,7 +1095,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr ... io.recvline() ... io.close() ... server.close() - b'Hello\n' + b'Hello' """ if context.noptrace: log.warn_once("Skipping debug attach since context.noptrace==True") diff --git a/pwnlib/rop/ret2dlresolve.py b/pwnlib/rop/ret2dlresolve.py index 2788a0936..c07b8eaa9 100644 --- a/pwnlib/rop/ret2dlresolve.py +++ b/pwnlib/rop/ret2dlresolve.py @@ -35,7 +35,7 @@ >>> p = elf.process() # doctest: +LINUX >>> p.sendline(fit({64+context.bytes*3: raw_rop, 200: dlresolve.payload})) # doctest: +LINUX >>> p.recvline() # doctest: +LINUX - b'pwned\n' + b'pwned' You can also use ``Ret2dlresolve`` on AMD64: @@ -61,7 +61,7 @@ >>> if dlresolve.unreliable: # doctest: +LINUX ... p.poll(True) == -signal.SIGSEGV ... else: - ... p.recvline() == b'pwned\n' + ... p.recvline() == b'pwned' True """ diff --git a/pwnlib/rop/rop.py b/pwnlib/rop/rop.py index d4963bd41..ffad1333c 100644 --- a/pwnlib/rop/rop.py +++ b/pwnlib/rop/rop.py @@ -355,7 +355,7 @@ >>> time.sleep(1) >>> p.sendline(b'echo hello; exit') >>> p.recvline() - b'hello\n' + b'hello' """ from __future__ import absolute_import from __future__ import division diff --git a/pwnlib/rop/srop.py b/pwnlib/rop/srop.py index 2d2a27f24..f4c1a997c 100644 --- a/pwnlib/rop/srop.py +++ b/pwnlib/rop/srop.py @@ -49,7 +49,7 @@ >>> p = process(binary.path) >>> p.send(bytes(frame)) >>> p.recvline() - b'Hello, World\n' + b'Hello, World' >>> p.poll(block=True) 0 @@ -75,7 +75,7 @@ >>> p = process(binary.path) >>> p.send(bytes(frame)) >>> p.recvline() - b'Hello, World\n' + b'Hello, World' >>> p.poll(block=True) 0 @@ -101,7 +101,7 @@ >>> p = process(binary.path) >>> p.send(bytes(frame)) >>> p.recvline() - b'Hello, World\n' + b'Hello, World' >>> p.wait_for_close() >>> p.poll(block=True) 0 @@ -128,7 +128,7 @@ >>> p = process(binary.path) >>> p.send(bytes(frame)) >>> p.recvline() - b'Hello, World\n' + b'Hello, World' >>> p.poll(block=True) 0 @@ -154,7 +154,7 @@ >>> p = process(binary.path) >>> p.send(bytes(frame)) >>> p.recvline() - b'Hello, World\n' + b'Hello, World' >>> p.poll(block=True) 0 diff --git a/pwnlib/shellcraft/templates/aarch64/darwin/cat.asm b/pwnlib/shellcraft/templates/aarch64/darwin/cat.asm index 9b889ae7e..13425d539 100644 --- a/pwnlib/shellcraft/templates/aarch64/darwin/cat.asm +++ b/pwnlib/shellcraft/templates/aarch64/darwin/cat.asm @@ -11,7 +11,7 @@ Example: >>> write(f, 'This is the flag\n') >>> shellcode = shellcraft.cat(f) + shellcraft.exit(0) >>> run_assembly(shellcode).recvline() - b'This is the flag\n' + b'This is the flag' <% if fd == 'x0': diff --git a/pwnlib/shellcraft/templates/aarch64/darwin/cat2.asm b/pwnlib/shellcraft/templates/aarch64/darwin/cat2.asm index de9e5817b..20e6408e6 100644 --- a/pwnlib/shellcraft/templates/aarch64/darwin/cat2.asm +++ b/pwnlib/shellcraft/templates/aarch64/darwin/cat2.asm @@ -12,7 +12,7 @@ Example: >>> write(f, 'This is the flag\n') >>> shellcode = shellcraft.cat2(f) + shellcraft.exit(0) >>> run_assembly(shellcode).recvline() - b'This is the flag\n' + b'This is the flag' <% if fd == 'x0': diff --git a/pwnlib/shellcraft/templates/aarch64/linux/cat.asm b/pwnlib/shellcraft/templates/aarch64/linux/cat.asm index f3ec3e0d7..25bf29dee 100644 --- a/pwnlib/shellcraft/templates/aarch64/linux/cat.asm +++ b/pwnlib/shellcraft/templates/aarch64/linux/cat.asm @@ -11,7 +11,7 @@ Example: >>> write(f, 'This is the flag\n') >>> shellcode = shellcraft.cat(f) + shellcraft.exit(0) >>> run_assembly(shellcode).recvline() - b'This is the flag\n' + b'This is the flag' <% if fd == 'x0': diff --git a/pwnlib/shellcraft/templates/aarch64/linux/cat2.asm b/pwnlib/shellcraft/templates/aarch64/linux/cat2.asm index de9e5817b..20e6408e6 100644 --- a/pwnlib/shellcraft/templates/aarch64/linux/cat2.asm +++ b/pwnlib/shellcraft/templates/aarch64/linux/cat2.asm @@ -12,7 +12,7 @@ Example: >>> write(f, 'This is the flag\n') >>> shellcode = shellcraft.cat2(f) + shellcraft.exit(0) >>> run_assembly(shellcode).recvline() - b'This is the flag\n' + b'This is the flag' <% if fd == 'x0': diff --git a/pwnlib/shellcraft/templates/aarch64/linux/echo.asm b/pwnlib/shellcraft/templates/aarch64/linux/echo.asm index 972bdddd7..0a89c1bda 100644 --- a/pwnlib/shellcraft/templates/aarch64/linux/echo.asm +++ b/pwnlib/shellcraft/templates/aarch64/linux/echo.asm @@ -6,7 +6,7 @@ Writes a string to a file descriptor Example: >>> run_assembly(shellcraft.echo('hello\n', 1)).recvline() - b'hello\n' + b'hello' diff --git a/pwnlib/shellcraft/templates/aarch64/linux/stage.asm b/pwnlib/shellcraft/templates/aarch64/linux/stage.asm index a90eaf36b..e99fa947a 100644 --- a/pwnlib/shellcraft/templates/aarch64/linux/stage.asm +++ b/pwnlib/shellcraft/templates/aarch64/linux/stage.asm @@ -22,7 +22,7 @@ Example: >>> p.pack(len(sc)) >>> p.send(sc) >>> p.recvline() - b'Hello\n' + b'Hello' <% protection = C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC diff --git a/pwnlib/shellcraft/templates/amd64/linux/stage.asm b/pwnlib/shellcraft/templates/amd64/linux/stage.asm index b06a3004e..a6683e936 100644 --- a/pwnlib/shellcraft/templates/amd64/linux/stage.asm +++ b/pwnlib/shellcraft/templates/amd64/linux/stage.asm @@ -22,7 +22,7 @@ Example: >>> p.pack(len(sc)) >>> p.send(sc) >>> p.recvline() - b'Hello\n' + b'Hello' <% protection = C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC diff --git a/pwnlib/shellcraft/templates/amd64/strcpy.asm b/pwnlib/shellcraft/templates/amd64/strcpy.asm index e93ac680d..5dd9fc279 100644 --- a/pwnlib/shellcraft/templates/amd64/strcpy.asm +++ b/pwnlib/shellcraft/templates/amd64/strcpy.asm @@ -16,7 +16,7 @@ Example: >>> sc += 'get_str: call pop_str\n' >>> sc += '.asciz "Hello, world\\n"' >>> run_assembly(sc).recvline() - b'Hello, world\n' + b'Hello, world' <%page args="dst, src"/> ${setregs({'rcx': -1, diff --git a/pwnlib/shellcraft/templates/arm/linux/cat.asm b/pwnlib/shellcraft/templates/arm/linux/cat.asm index cda769923..dcfc0fac3 100644 --- a/pwnlib/shellcraft/templates/arm/linux/cat.asm +++ b/pwnlib/shellcraft/templates/arm/linux/cat.asm @@ -11,7 +11,7 @@ Example: >>> f = tempfile.mktemp() >>> write(f, 'FLAG\n') >>> run_assembly(shellcraft.arm.linux.cat(f)).recvline() - b'FLAG\n' + b'FLAG' ${arm.pushstr(filename)} diff --git a/pwnlib/shellcraft/templates/arm/linux/cat2.asm b/pwnlib/shellcraft/templates/arm/linux/cat2.asm index 9055e7d13..ebaee137e 100644 --- a/pwnlib/shellcraft/templates/arm/linux/cat2.asm +++ b/pwnlib/shellcraft/templates/arm/linux/cat2.asm @@ -12,7 +12,7 @@ Example: >>> f = tempfile.mktemp() >>> write(f, 'FLAG\n') >>> run_assembly(shellcraft.arm.linux.cat2(f)).recvline() - b'FLAG\n' + b'FLAG' ${arm.pushstr(filename)} diff --git a/pwnlib/shellcraft/templates/arm/linux/echo.asm b/pwnlib/shellcraft/templates/arm/linux/echo.asm index 2980eabb5..4e1e3a136 100644 --- a/pwnlib/shellcraft/templates/arm/linux/echo.asm +++ b/pwnlib/shellcraft/templates/arm/linux/echo.asm @@ -6,7 +6,7 @@ Writes a string to a file descriptor Example: >>> run_assembly(shellcraft.echo('hello\n', 1)).recvline() - b'hello\n' + b'hello' diff --git a/pwnlib/shellcraft/templates/i386/linux/stage.asm b/pwnlib/shellcraft/templates/i386/linux/stage.asm index bc781243b..6fbe6c70a 100644 --- a/pwnlib/shellcraft/templates/i386/linux/stage.asm +++ b/pwnlib/shellcraft/templates/i386/linux/stage.asm @@ -22,7 +22,7 @@ Example: >>> p.pack(len(sc)) >>> p.send(sc) >>> p.recvline() - b'Hello\n' + b'Hello' <% diff --git a/pwnlib/shellcraft/templates/i386/strcpy.asm b/pwnlib/shellcraft/templates/i386/strcpy.asm index b5804face..d4b07f085 100644 --- a/pwnlib/shellcraft/templates/i386/strcpy.asm +++ b/pwnlib/shellcraft/templates/i386/strcpy.asm @@ -16,7 +16,7 @@ Example: >>> sc += 'get_str: call pop_str\n' >>> sc += '.asciz "Hello, world\\n"' >>> run_assembly(sc).recvline() - b'Hello, world\n' + b'Hello, world' <%page args="dst, src"/> ${setregs({'esi': src, diff --git a/pwnlib/shellcraft/templates/thumb/linux/cat.asm b/pwnlib/shellcraft/templates/thumb/linux/cat.asm index dbae80fb5..7c0a72738 100644 --- a/pwnlib/shellcraft/templates/thumb/linux/cat.asm +++ b/pwnlib/shellcraft/templates/thumb/linux/cat.asm @@ -12,7 +12,7 @@ Example: >>> f = tempfile.mktemp() >>> write(f, 'FLAG\n') >>> run_assembly(shellcraft.arm.to_thumb()+shellcraft.thumb.linux.cat(f)).recvline() - b'FLAG\n' + b'FLAG' <% diff --git a/pwnlib/shellcraft/templates/thumb/linux/cat2.asm b/pwnlib/shellcraft/templates/thumb/linux/cat2.asm index 940894036..c2b93223a 100644 --- a/pwnlib/shellcraft/templates/thumb/linux/cat2.asm +++ b/pwnlib/shellcraft/templates/thumb/linux/cat2.asm @@ -13,7 +13,7 @@ Example: >>> f = tempfile.mktemp() >>> write(f, 'FLAG\n') >>> run_assembly(shellcraft.arm.to_thumb()+shellcraft.thumb.linux.cat2(f)).recvline() - b'FLAG\n' + b'FLAG' diff --git a/pwnlib/shellcraft/templates/thumb/linux/echo.asm b/pwnlib/shellcraft/templates/thumb/linux/echo.asm index c5fb15ab0..f6e0720d2 100644 --- a/pwnlib/shellcraft/templates/thumb/linux/echo.asm +++ b/pwnlib/shellcraft/templates/thumb/linux/echo.asm @@ -6,7 +6,7 @@ Writes a string to a file descriptor Example: >>> run_assembly(shellcraft.echo('hello\n', 1)).recvline() - b'hello\n' + b'hello' diff --git a/pwnlib/shellcraft/templates/thumb/linux/stage.asm b/pwnlib/shellcraft/templates/thumb/linux/stage.asm index 635c5bf7f..e96e2dad1 100644 --- a/pwnlib/shellcraft/templates/thumb/linux/stage.asm +++ b/pwnlib/shellcraft/templates/thumb/linux/stage.asm @@ -22,7 +22,7 @@ Example: >>> p.pack(len(sc)) >>> p.send(sc) >>> p.recvline() - b'Hello\n' + b'Hello' <% protection = C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC diff --git a/pwnlib/tubes/listen.py b/pwnlib/tubes/listen.py index 08bae02c8..e38f56c0a 100644 --- a/pwnlib/tubes/listen.py +++ b/pwnlib/tubes/listen.py @@ -34,7 +34,7 @@ class listen(sock): >>> _ = l.wait_for_connection() >>> l.sendline(b'Hello') >>> r.recvline() - b'Hello\n' + b'Hello' .. doctest:: :options: +POSIX +TODO @@ -45,7 +45,7 @@ class listen(sock): >>> r = remote('127.0.0.1', l.lport) >>> r.sendline(b'echo Goodbye') >>> r.recvline() - b'Goodbye\n' + b'Goodbye' >>> # and it works with ipv6 by defaut, too! >>> l = listen() @@ -53,7 +53,7 @@ class listen(sock): >>> _ = l.wait_for_connection() >>> r.sendline(b'Bye-bye') >>> l.recvline() - b'Bye-bye\n' + b'Bye-bye' """ #: Local port diff --git a/pwnlib/tubes/process.py b/pwnlib/tubes/process.py index c53ab2e5c..c0180bae9 100644 --- a/pwnlib/tubes/process.py +++ b/pwnlib/tubes/process.py @@ -136,7 +136,7 @@ class process(tube): >>> p.connected('send') False >>> p.recvline() - b'Hello world\n' + b'Hello world' >>> p.recvuntil(b',') b'Wow,' >>> p.recvregex(b'.*data') @@ -170,7 +170,7 @@ class process(tube): ... preexec_fn = lambda: os.dup2(0,2)) >>> p.sendline(b'hello') >>> p.recvline() - b'hello\n' + b'hello' >>> stack_smashing = ['python','-c','open("/dev/tty","wb").write(b"stack smashing detected")'] >>> process(stack_smashing).recvall() @@ -203,7 +203,7 @@ class process(tube): True >>> process(['sh','-c','ulimit -s'], aslr=0).recvline() - b'unlimited\n' + b'unlimited' >>> io = process(['sh','-c','sleep 10; exit 7'], alarm=2) >>> io.poll(block=True) == -signal.SIGALRM @@ -904,7 +904,7 @@ def maps(self): >>> p = process(['cat']) >>> p.sendline(b"meow") >>> p.recvline() - b'meow\\n' + b'meow' >>> proc_maps = open("/proc/" + str(p.pid) + "/maps", "r").readlines() >>> pwn_maps = p.maps() >>> len(proc_maps) == len(pwn_maps) @@ -1055,7 +1055,7 @@ def heap_mapping(self, single=True): >>> p = process(['cat']) >>> p.sendline(b'meow') >>> p.recvline() - b'meow\\n' + b'meow' >>> mapping = p.heap_mapping() >>> mapping.path '[heap]' @@ -1146,7 +1146,7 @@ def libc_mapping(self, single=True): >>> p = process(['cat']) >>> p.sendline(b'meow') >>> p.recvline() - b'meow\\n' + b'meow' >>> mapping = p.libc_mapping() >>> mapping.path # doctest: +ELLIPSIS '...libc...' @@ -1226,7 +1226,7 @@ def elf_mapping(self, single=True): >>> p = process(['cat']) >>> p.sendline(b'meow') >>> p.recvline() - b'meow\\n' + b'meow' >>> mapping = p.elf_mapping() >>> mapping.path # doctest: +ELLIPSIS '...cat...' @@ -1301,7 +1301,7 @@ def address_mapping(self, address): >>> p = process(['cat']) >>> p.sendline(b'meow') >>> p.recvline() - b'meow\\n' + b'meow' >>> libc = p.libc_mapping().address >>> heap = p.heap_mapping().address >>> elf = p.elf_mapping().address diff --git a/pwnlib/tubes/server.py b/pwnlib/tubes/server.py index 5bb5deb3b..575dd0c7c 100644 --- a/pwnlib/tubes/server.py +++ b/pwnlib/tubes/server.py @@ -33,7 +33,7 @@ class server(sock): >>> server_conn = s.next_connection() >>> client_conn.sendline(b'Hello') >>> server_conn.recvline() - b'Hello\n' + b'Hello' >>> def cb(r): ... client_input = r.readline() ... r.send(client_input[::-1]) @@ -42,7 +42,7 @@ class server(sock): >>> client_conn = remote('localhost', t.lport) >>> client_conn.sendline(b'callback') >>> client_conn.recv() - b'\nkcabllac' + b'kcabllac' """ #: Local port diff --git a/pwnlib/tubes/ssh.py b/pwnlib/tubes/ssh.py index 686d2bea3..60c315057 100644 --- a/pwnlib/tubes/ssh.py +++ b/pwnlib/tubes/ssh.py @@ -944,17 +944,17 @@ def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, igno >>> io = s.process([], executable='sh') >>> io.sendline(b'echo $0') >>> io.recvline() - b'$ \n' + b'$ ' >>> # Make sure that we have a shell >>> io.sendline(b'echo hello') >>> io.recvline() - b'$ hello\n' + b'$ hello' >>> # Testing that empty argv[0] works >>> io = s.process([''], executable='sh') >>> io.sendline(b'echo $0') >>> io.recvline() - b'$ \n' + b'$ ' """ cwd = cwd or self.cwd @@ -1093,7 +1093,7 @@ def system(self, process, tty = True, cwd = None, env = None, timeout = None, ra >>> py.sendline(b'print(2+2)') >>> py.sendline(b'exit()') >>> print(repr(py.recvline())) - b'4\n' + b'4' >>> s.system('env | grep -a AAAA', env={'AAAA': b'\x90'}).recvall() b'AAAA=\x90\n' >>> io = s.system('pwd', cwd='/tmp') @@ -1191,7 +1191,7 @@ def connect_remote(self, host, port, timeout = Timeout.default): >>> a=a; b = l.wait_for_connection() # a=a; prevents hangs >>> a.sendline(b'Hello') >>> print(repr(b.recvline())) - b'Hello\n' + b'Hello' """ return ssh_connecter(self, host, port, timeout, level=self.level) @@ -1215,7 +1215,7 @@ def listen_remote(self, port = 0, bind_address = '', timeout = Timeout.default): >>> a=a; b = l.wait_for_connection() # a=a; prevents hangs >>> a.sendline(b'Hello') >>> print(repr(b.recvline())) - b'Hello\n' + b'Hello' """ return ssh_listener(self, bind_address, port, timeout, level=self.level) @@ -1916,12 +1916,12 @@ def preexec(): with self.process('true', preexec_fn=preexec) as io: self._platform_info = { - 'system': io.recvline().lower().strip().decode(), - 'node': io.recvline().lower().strip().decode(), - 'release': io.recvline().lower().strip().decode(), - 'version': io.recvline().lower().strip().decode(), - 'machine': io.recvline().lower().strip().decode(), - 'processor': io.recvline().lower().strip().decode(), + 'system': io.recvlineS().lower(), + 'node': io.recvlineS().lower(), + 'release': io.recvlineS().lower(), + 'version': io.recvlineS().lower(), + 'machine': io.recvlineS().lower(), + 'processor': io.recvlineS().lower(), 'distro': 'Unknown', 'distro_ver': '' } diff --git a/pwnlib/tubes/tube.py b/pwnlib/tubes/tube.py index 47afa0055..b7943d641 100644 --- a/pwnlib/tubes/tube.py +++ b/pwnlib/tubes/tube.py @@ -89,7 +89,7 @@ def newline(self): >>> t.newline = b'X' >>> t.unrecv(b'A\nB\nCX') >>> t.recvline() - b'A\nB\nCX' + b'A\nB\nC' >>> t = tube() >>> context.newline = b'\r\n' @@ -504,7 +504,7 @@ def recvlinesb(self, numlines=2**20, keepends=None, drop=None, timeout=default): return [bytearray(x) for x in self.recvlines(numlines, keepends=keepends, drop=drop, timeout=timeout)] def recvline(self, keepends=None, drop=None, timeout=default): - r"""recvline(drop=False, timeout=default) -> bytes + r"""recvline(drop=True, timeout=default) -> bytes Receive a single line from the tube. @@ -519,8 +519,12 @@ def recvline(self, keepends=None, drop=None, timeout=default): If the request is not satisfied before ``timeout`` seconds pass, all data is buffered and an empty byte string (``b''``) is returned. + Note: The default value for ``drop`` was changed to :const:`True` in + version 5.0.0 to drop the trailing newline. + + Arguments: - drop(bool): Drop the line ending (:const:`False`). + drop(bool): Drop the line ending (:const:`True`). timeout(int): Timeout Raises: @@ -540,11 +544,11 @@ def recvline(self, keepends=None, drop=None, timeout=default): >>> t = tube() >>> t.recv_raw = lambda n: b'Foo\nBar\r\nBaz\n' >>> t.recvline() - b'Foo\n' + b'Foo' >>> t.recvline() - b'Bar\r\n' - >>> t.recvline(False) - b'Baz' + b'Bar\r' + >>> t.recvline(drop=False) + b'Baz\n' >>> t.newline = b'\r\n' >>> t.recvline(drop=True) b'Foo\nBar' @@ -557,7 +561,7 @@ def recvline(self, keepends=None, drop=None, timeout=default): >>> _recv_eof.throw = False >>> t.recv_raw = _recv_eof >>> t.recvline() - b'real line\n' + b'real line' >>> t.recvline() b'trailing data' >>> t.recvline() @@ -565,7 +569,7 @@ def recvline(self, keepends=None, drop=None, timeout=default): ... EOFError """ - drop = self._normalize_keepends_drop(keepends, drop, False) + drop = self._normalize_keepends_drop(keepends, drop, True) del keepends try: @@ -1172,7 +1176,7 @@ def upload_manually(self, data, target_path = './payload', prompt = b'$', chunk_ >>> r.upload_manually(b'some\\xca\\xfedata\\n', prompt=b'', chmod_flags='') >>> r.sendline(b'cat ./payload') >>> r.recvline() - b'some\\xca\\xfedata\\n' + b'some\\xca\\xfedata' >>> r.upload_manually(cyclic(0x1000), target_path='./cyclic_pattern', prompt=b'', chunk_size=0x10, compression='gzip') >>> r.sendline(b'sha256sum ./cyclic_pattern') @@ -1183,7 +1187,7 @@ def upload_manually(self, data, target_path = './payload', prompt = b'$', chunk_ >>> r.upload_manually(blob.data, prompt=b'') >>> r.sendline(b'./payload') >>> r.recvline() - b'Hello world!\\n' + b'Hello world!' >>> r.close() >>> l.close() """ diff --git a/pwnlib/util/misc.py b/pwnlib/util/misc.py index 0b99a599b..5e73f01c6 100644 --- a/pwnlib/util/misc.py +++ b/pwnlib/util/misc.py @@ -585,14 +585,14 @@ def dealarm_shell(tube): """ tube.clean() - tube.sendline('which python || echo') - if tube.recvline().startswith('/'): - tube.sendline('''exec python -c "import signal, os; signal.alarm(0); os.execl('$SHELL','')"''') + tube.sendline(b'which python || echo') + if tube.recvline().startswith(b'/'): + tube.sendline(b'''exec python -c "import signal, os; signal.alarm(0); os.execl('$SHELL','')"''') return tube - tube.sendline('which perl || echo') - if tube.recvline().startswith('/'): - tube.sendline('''exec perl -e "alarm 0; exec '${SHELL:-/bin/sh}'"''') + tube.sendline(b'which perl || echo') + if tube.recvline().startswith(b'/'): + tube.sendline(b'''exec perl -e "alarm 0; exec '${SHELL:-/bin/sh}'"''') return tube return None From a855f3b0d91433e53932567ade7278fd2e4b708e Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Thu, 19 Jun 2025 18:28:49 +0200 Subject: [PATCH 2/3] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dd23f2a5..e929b7a0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ The table below shows which release corresponds to each branch, and what date th - [#2574][2574] Allow creating an ELF from in-memory bytes - [#2575][2575] Detect when Terminator is being used as terminal - [#2578][2578] Add gnome-terminal, Alacritty, Ttilix for run_in_new_terminal +- [#2588][2588] Drop newline in `tube.recvline` by default [2419]: https://github.com/Gallopsled/pwntools/pull/2419 [2551]: https://github.com/Gallopsled/pwntools/pull/2551 @@ -112,6 +113,7 @@ The table below shows which release corresponds to each branch, and what date th [2574]: https://github.com/Gallopsled/pwntools/pull/2574 [2575]: https://github.com/Gallopsled/pwntools/pull/2575 [2578]: https://github.com/Gallopsled/pwntools/pull/2578 +[2588]: https://github.com/Gallopsled/pwntools/pull/2588 ## 4.15.0 (`beta`) From 44289e3772d699f1868e27d548799071cd9764b7 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Sat, 6 Sep 2025 15:36:59 +0200 Subject: [PATCH 3/3] Use `sendline` in server callback example --- pwnlib/tubes/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pwnlib/tubes/server.py b/pwnlib/tubes/server.py index 575dd0c7c..b9494be34 100644 --- a/pwnlib/tubes/server.py +++ b/pwnlib/tubes/server.py @@ -35,13 +35,13 @@ class server(sock): >>> server_conn.recvline() b'Hello' >>> def cb(r): - ... client_input = r.readline() - ... r.send(client_input[::-1]) + ... client_input = r.recvline() + ... r.sendline(client_input[::-1]) ... >>> t = server(8889, callback=cb) >>> client_conn = remote('localhost', t.lport) >>> client_conn.sendline(b'callback') - >>> client_conn.recv() + >>> client_conn.recvline() b'kcabllac' """