sqli-labs-master 注入详解

 SQLI-LABS SERIES PART - 2,3,4,5
In the first part of the series we downloaded the PHP code files and installed them on the backtrack machine or under XAMPP on windows.
第一部分:搭建测试环境

Error based SQL injections:报错注入
Error Based Sql injection is called so because in this errors are being displaded on the web page, and these errors are used to discover the underlying query.
报错注入:是因为网页把错误展示出来,并且这些错误是用来发现潜在查询的。

What exactly is SQL injection? 什么是sql注入
SQL injection is a technique often used to attack databases through a website. SQL injection is a code injection technique that exploits a security vulnerability in a websites software. -- source wikipedia
SQL注入是经常被用来通过一个网站来攻击数据库的技术。 SQL注入是利用在网站的软件安全漏洞的代码注入技术。 - 源维基百科

How does SQL injection happen? Sql注入发生的原因
Let us take the example of Less-1, the webpage is taking an input through the parameter "ID" and passes it on to the backend database by constructing a query in real time.
打开Less-1,网页是通过参数“ID”取输入,并把它送到后端数据库被实时地构造查询。


Less-1:第一课
if you open the source of the index.php under Less-1, you would see on line 29:打开Less-1的index.php的第29行
$sql="SELECT * FROM users WHERE id=‘$id‘ LIMIT 0,1";  查询语句
in here we see that the variable $id is being wrapped around single quotes.  发现id没有任何过滤;
id =  $id  , Now when a  tester provides a   or 1=1 then it becomes id =   or 1=1    thereby effectively evaluating the complete query as id= empty string  or (evaluate one equals one) and escaping the data boundary and getting executed as code. As there is a quote on right side which we either need to handle or comment out remaining part of query.

构造查询语句
     or 1=1 Will work nicely.   id =   or 1=1  LIMIT 0,1
     or 1=1 --+ Will also work.   id =   or 1=1 --+   commented out
     or 1=1 # will also work.       id =   or 1=1 #  commented out

Less-2 line number 31,32  第二课
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

    or 1=1  Will work nicely.   id =   or 1=1  LIMIT 0,1
    or 1=1 --+ Will also work. id =   or 1=1 --+ 
or 1=1 # Will also work.    id =   or 1=1 #

 


SQLI-LABS SERIES PART 6,7

DOUBLE QUERY INJECTIONS OR SUBQUERY INJECTIONS:二次注入或子查询注入

In the last 5 parts of the series we learnt about some basics about the error based injections and used the UNION statements to dump the database using the web application. Well we could achieve it because the database was interacting with web page and some database fields were visible on the web pages. A basic injection looked like id=-1 union all select 1,2,3 --+ and we were able to see the username name and password field displaying value 2 and 3.
在过去的5个部分的系列,我们了解了有关基于错误注入一些基础知识和使用UNION语句来使用Web应用程序的数据库。好了,我们可以实现它,因为数据库是使用网页交互和一些数据库领域是在网页上可见的。基本注入看起来ID=-1联盟的所有选择1,2,3 - +,我们能够看到的用户名名和密码字段中显示的值2和3。

In a scenario when the database does not directly display columns on the web page, then the above technique cannot be used.
在当数据库结果不直接显示web页面的情况下,则上述技术不能被使用。

As we see we just see a generic message "You are in".  Therefore in this case, the database is not displaying any files on the page. In this case only way the database is displaying into is through the mysql error. (note: I am interchangeably using the Lesson 5 and 6, only thing different is way to produce error)
正如我们看到的,我们只看到一个通用的消息:“You are in”。因此,在这种情况下,数据库结果没有显示在页面上。在这种情况下,唯一的办法数据库显示成是通过MySQL错误。(注:我可以互换使用的第5课和第6课,唯一不同的就是这样产生的误差)


So primary objective in a double query injection is to create a query injection in such a way which is syntactically correct (correct at compile time) but produce an error at run time thereby spitting useful information in the errors. In case of MSSQL server cast errors dumps the info but in case of MYSQL, being flexible returns empty rows. Therefore some genius researchers found a combination of use of aggregate functions, group by clause, and use of random functions to produce errors are run time due to dynamic calculations involved in random function and aggregate function like count. 
在二次SQL注入等等主要目的是在这样一种方式是语法正确(在编译时是正确的)创建一个查询,产生注入,但在运行时出现错误,从而获取错误的有用信息。如果MSSQL服务器存放错误的转储信息,但如果是灵活的返回空行(这里的翻译有问题,希望大牛指教)。因此,一些天才的研究人员发现,使用聚合函数,group by子句,并利用随机函数产生错误运行时,由于涉及的随机函数和聚合函数计算;

 Less-5 line number 29 第五课
$sql="SELECT * FROM users WHERE id=‘$id‘ LIMIT 0,1";

     or 1=1 Will work nicely.   id =   or 1=1  LIMIT 0,1
     or 1=1 --+ Will also work.   id =   or 1=1 --+   commented out
     or 1=1 # will also work.   id =   or 1=1 #  commented out

Less-6 line number 28,29 第六课
$id = ".$id.";
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

    " or "1"="1 Will work nicely.   id = " " or "1"="1 " LIMIT 0,1
    " or 1=1 --+ Will also work.   id =  " " or 1=1 --+ " commented out
    " or 1=1 # will also work.       id = " " or 1=1 # " commented out


SQLI-LABS SERIES PART-8

BLIND INJECTIONS:盲注

Continuing the SQLI-LABS series, we discussed the Error based injections, and discussed Union type injections and double query injections. In todays post we would be discussing Blind injections. Blind injections got this name because during blind injections, you do not get any help from the application. All the errors are suppressed from the end user.
继续SQLI-LABS系列中,我们讨论了基于注入错误,并讨论了联合型注入剂和二次注入。在今天的文章中,我们将讨论盲注。盲注这个名字,因为在盲目进行,你不会从应用程序的任何帮助。所有的错误都来自终端用户抑制。
Therefore complete injection is based on a guess. The tester does not see any error responses to tune his  injections.
因此,注入完全是基于猜测。该测试没有看到自己注入语句返回的任何错误响应。
Blind Injections can be classified mainly into two categories:盲注的分类
Boolean based Blind injections:基于bool的盲注
        Time based Blind injections.:基于时间的盲注

Boolean Based:布尔类型的盲注
As per wikipedia "In computer science, the Boolean or logical data type is a data type, having two values (usually denoted true and false), intended to represent the truth values of logic and Boolean algebra. It is named after George Boole, who first defined an algebraic system of logic in the mid 19th century".
按照维基百科“在计算机科学中,布尔逻辑或数据类型的数据类型,有两个值(通常表示true和false),意在表示逻辑和布尔代数的真值。乔治·布尔,之后它被命名谁第一个定义的逻辑,在19世纪中期的代数系统“。
Well in certain web applications, you can witness that the database does not write any fields on the web page or somehow union injections do not work, and the mysql errors are also not displayed on the page, so technically there is no direct channel through which the database writes on web page. In this case the only option left is to use blind injections. With Blind injections we cannot dump the strings or names directly but need to deduce names character by character.
以及在特定的Web应用程序,你可以亲眼目睹该数据库不显示任何事物在网页上或在某种方式联合注入不显示,MySQL的错误也不会显示在页面上,所以在技术上没有通过任何直接的渠道把数据库回显写在网页上。在这种情况下,剩下的唯一选择是使用盲注。盲注,我们不能直接倾倒字符串或名称,但需要通过角色来演绎角色的名字。
In general when we were dealing with error based injections, we ask the database questions like, dump us the database name, version, table names etc. In case of blind injections, we change the way we ask questions to database and rephrase questions like is the first letter of the database this? and answer comes out as either yes or no or true or false. 
一般来说,当我们处理基于错误注入,我们问这样的数据库的问题,我们转储数据库的名称,版本,表名等。如果盲注,我们改变我们提问到数据库的方式,另一种方式的问题,比如是在这个数据库的第一个字母?并回答出来的是或否或真或假。

Less-8 line number 29
$sql="SELECT * FROM users WHERE id=‘$id‘ LIMIT 0,1";
basic injection example:
1 AND 1=1 -- returns true
1 AND 1=0  -- returns false
Therefore by evaluating the strings character by character we can dump the complete database.
因此由性格评估字符串的字符,我们可以转储整个数据库。


SQLI-LABS SERIES PART 9

TIME BASED INJECTIONS:时间注入

In the previous post, we discussed the basics of Blind injections and started to explore Boolean based blind injections. In this blog post we would continue with the Blind injections and discuss TIME based injections. 
在前面的文章中,我们讨论了盲注资的基础知识,并开始探索基于布尔盲注。在这篇博客文章中,我们将继续与盲注,并讨论基于时间的注射。
In certain web web applications, which are vulnerable but does not disclose errors, nor does the database display any fields on the web pages and neither does it react to physical boolean queries of yes and no meaning we cannot physically differentiate between true and false,in that case, we can use time to distinguish between true and false. This can be achieved by using sleep() function. This function is non cpu intensive and if query is true will wait for some time before returning a response and respond quickly if false. This time difference in page reload gives us the correct characters one by one.
在某些网页的Web应用程序,它是脆弱的,但没有透露错误,也不在网页上显示数据库任何信息,甚至没有bool类型的正确判断,并没有任何意义,我们不能在物理真假区分,在这种情况下,我们可以用时间真假分辨。这可以通过使用sleep()函数来实现。此功能非CPU密集型的,如果查询是真的会等待一段时间返回响应之前,如果假的快速响应。在页面重载这个时间差给了我们正确的字符一个接一个。
Another way to do time based injections is by use of heavy queries (benchmark queries) which are intensive and consume some CPU cycles if query returns true and are quick if it is false. It is always good to use sleep() function.
另一种方法做基于时间的注射是使用重查询(查询基准),这是密集的,消耗一些CPU周期,如果查询返回真,很快,如果它是假的。这是一件好事,用sleep()函数。

Less - 9  line number 29
$sql="SELECT * FROM users WHERE id=‘$id‘ LIMIT 0,1";
1+and+sleep(10)+--+  -- Basic injection to detect sqli
1+and+if(1=1, sleep(10), null)+--+  -- Returns True  ( page load is approx 10 sec)
1+and+if(1=0, sleep(10), null)+--+  -- Returns False (page load is almost instant)


Less-10 line number 28,29
$id = ".$id.";
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
1"+and+sleep(10)+--+  -- Basic injection to detect sqli
1"+and+if(1=1, sleep(10), null)+--+  -- Returns True  ( page load is approx 10 sec)
1"+and+if(1=0, sleep(10), null)+--+  -- Returns False (page load is almost instant)



SQLI-LABS SERIES PART-10

outfile function or dumpfile function:outfile()函数注入

USING THE OUTFILE/DUMPFILE:使用outfile或dunpfile

Well if the connection user which is configured to run the queries to the back-end DB has the privileges to write to file system (webroot of server or any other folder under it), then in that case, we can build queries and use the inbuilt function called outfile or dumpfile.
example query: select * from table into outfile "path-to-file/filename"
那么,如果其被配置在连接用户运行查询后端DB具有特权写入到文件系统(服务器或根据任何其他文件夹的根目录),那么在这种情况下,我们可以构造查询,并使用内置的函数调用输出文件或转储文件。 
例如查询:select * from table into outfile "path-to-file/filename"
There are two functions which can be used in this case, outfile and dumpfile. With dumpfile, it dumps only one row without any formatting details. This is specially important if you are playing with binary data. The outfile preserves the formatting, carriage returns, etc and dumps multiple rows.
还有,可以在这种情况下,输出文件和转储文件可以使用两种功能。与转储文件,它转储只有一行不带任何格式的详细信息。这是特别重要的,如果你在玩的二进制数据。该输出文件保存格式,回车等,并转储多行。
same way we can use mysql to read files from the file system. for that we can use the function called load_file(). By default we cannot execute system commands through mysql, but if mysql is misconfigured, then can lead to upload of User Defined Functions which can lead to a complete compromise of server.
同样,我们可以用mysql从文件系统中读取文件。为此,我们可以使用函数调用LOAD_FILE()。默认情况下,我们无法通过执行mysql的系统命令,但是如果MySQL配置不正确,则可能会导致上传用户定义函数,可导致服务器的一个完整的妥协。


example injection may look like:  例如
 union select 1,load_file("/etc/passwd"),3 into dumpfile "/var/www/test.txt"
Less -7 Line number 31
$sql="SELECT * FROM users WHERE id=((‘$id‘)) LIMIT 0,1";
1))+or+1=1--+  -- Basic injection to detect sqli




SQLI-LABS SERIES PART-11
Today we will discuss about the injections in the forms. In the last 11 lessons of the series we discussed  injections via the GET request. When I had started to learn this topic, I realized that a lot of stuff was available on internet for the GET based injections but POST injections were rarely discussed. That increased curiosity in me much and this was the point when i started this lab. The forms , lesson 11 to 20 are different implementations similar to GET method. We would cover :
今天我们将讨论有关在形式注射。在本系列的最后11个课程,我们通过GET请求所讨论的注入。当我开始学习这个话题,我意识到,在互联网上有很多东西是可用于基于GET注入,但POST注入很少讨论。在我的好奇增加了很多,这是一点,当我开始这个实验。11-20是不同的实现方式类似于get方法。我们将覆盖(将开始讨论POST注入)
    Error based injections:报错注入
    Double Query injections:二次注入
    Boolean based Blind injections:布尔盲注
    Time based Blind Injections:时间盲注
    Injections in update query:执行更新注入
    Injections in the Headers:refer头注入
    Injections in cookies:cookie注入

POST BASED INJECTIONS:post注入
In general, nothing changes with the change in injection point. The logic for injection remains the same, the process remains the same. In a general scenario we take up Less-11 and 12 for this. As the page displays a login field. therefore we can build our first sudo query;
在一般情况下,没有用在注入点的变化而变化。用于注射的逻辑保持相同,则过程保持相同。在一般情况下,我们采取了11和12课这一点。由于页面显示登录域。因此,我们可以建立我们的第一个sudo的查询:
SELECT * FROM table WHERE username="$uname" and password="$password".

The basic steps involved in the testing to exploitation are:
参与测试开发的基本步骤如下:
1.Fuzzing: Try to give random inputs to all points where it is accepted, be creative and send the values which the developer has failed to visualize. Objective is to try to break the underlying query and gain more insite on how the query is formulated by reviewing error.
1,模糊测试:尽量给随机输入到被接受,勇于创新,发送该开发商并没有想象中的值的所有点。目的是尝试打破基础查询,并获得关于如何查询是通过检查错误制定更INSITE。
2.Then try to fix the query by either providing the extra characters to balance off what we injected to break the query or comment off rest of query in such a way that it gets fixed.
2,然后,尝试通过提供额外的字符,以平衡掉了我们注入到中断查询或以这样一种方式,它被固定评断的查询其余修正该查询。
3.Once we successfully achieved the above, we effectively get the left side, and right side of the injection and sql statement we inject in between these gets executed on backend.
3.一旦我们成功地实现了上述情况,我们有效地获得左侧和右侧,我们投放在这些之间的注入和SQL语句获取对后端执行。

As discussed in the previous post on error based injections, we used the UNION SELECT to dump the DB because the database layer was interacting with the web page and columns from table were visible on screen. same way we can see that a successful login leads to display of the username and password.
正如在以前的帖子上基于错误注入的讨论,我们用UNION SELECT转储数据库,因为数据库层用网页和列从表中的互动都显示在屏幕上。同样,我们可以看到,一个成功的登录会导致显示的用户名和密码。 
This can then be used to dump the database.
这可以被用来转储数据库。

Less-11 line number 57
@$sql="SELECT username, password FROM users WHERE username=‘$uname‘ and password=‘$passwd‘ LIMIT 0,1";
 or 1=1 Will work nicely if injected in username and password field.   username =   or 1=1  AND password =    or 1=1   
 or 1=1 --+ Will also work only in one field.   username =   or 1=1 --+   commented out
 or 1=1 # will also work only in one field.       username =   or 1=1 #  commented out


Less-12 Line 57,58,59
$uname=".$uname.";
$passwd=".$passwd."; 
@$sql="SELECT username, password FROM users WHERE username=($uname) and password=($passwd) LIMIT 0,1";

") or ("1")=("1 Will work nicely.   username = (" ")or ("1")=("1 ") AND password = (" ")or ("1")=("1 ") 
") or 1=1 --+ Will also work.   username = (" ")or 1=1 --+  ") commented out
") or 1=1 # will also work.       username = (" ") or 1=1 #  ") commented out



 SQLI-LABS SERIES PART 12
In the last part we started to explore the error based sql injections in the POST parameters. We were able to use the UNION statements to dump the database of the test bed. In a situation when the database does not interact with the web page and only was the database displays some info on the web page is through the mysql errors. Then in this case the fastest way to extract the info is through use of type caste errors involving sub queries also referred to as double queries, but MYSQL is very flexible and returns empty rows rather than throwing an error, so infosec gurus figured out some combinations of functions, if combined make sql queries which pass the compile time check but throw run-time errors. With the errors dumps the useful info which is needed.
在最后一部分,我们开始探索在POST参数基于错误的SQL注射。我们可以使用UNION语句来转储试验台的数据库。在这种情况时,该数据库不与网页交互,并仅是在数据库显示在网页上的一些信息是通过MySQL的错误。那么在这种情况下,提取的信息以最快的方式就是通过使用包括子查询,也被称为二次查询类型种姓的错误,但MySQL是非常灵活的,会返回空行,而不是抛出一个错误,因此信息安全大师的想通了一些组合的功能,如果它通过了编译时检查,但抛出运行时错误结合使SQL查询。随着错误转储所需要的有用的信息。

DOUBLE QUERY INJECTIONS:二次注入

As these involve sub query injections, the focus is to use the internal query to dump the info we want and wrap around a query which is syntactically correct, passing the compile time checks and produces error at run time and in process spit the core query as part of error.
由于这些涉及到的子查询注入,重点是使用内部查询转储我们想要的信息,并环绕查询的语法正确,经过编译时检查,并在运行时产生的错误,并在过程中吐出的核心查询作为错误的部分。

Basic query to cause injections :基于查询造成的注入

Less-13 line number 57
@$sql="SELECT username, password FROM users WHERE username=(‘$uname‘) and password=(‘$passwd‘) LIMIT 0,1";
) or (1)=(1 Will work nicely if injected in username and password field.   username = ( ) or (1)=(1 ) AND password = (  ) or (1)=(1   )
) or 1=1 --+ Will also work only in one field.   username = ( ) or 1=1 --+   commented out
) or 1=1 # will also work only in one field.       username = ( ) or 1=1 #  commented out

Less-14 Line 57,58,59
$uname=".$uname.";
$passwd=".$passwd."; 
@$sql="SELECT username, password FROM users WHERE username=$uname and password=$passwd LIMIT 0,1";
" or "1"="1 Will work nicely.   username = " " or "1"="1 " AND password = " " or "1"= "1 " 
" or 1=1 --+ Will also work.   username = " " or 1=1 --+  " commented out
" or 1=1 # will also work.       username = " " or 1=1 #  " commented out



SQLI-LABS SERIES PART 13

INJECTION IN UPDATE QUERY:执行更新注入

In the earlier parts of the series we looked at the GET and POST based injections and dived into details on error based SQL Injections (string type, Integer type), Double Query injections error based, Blind injections (Boolean based and Time based) or use the outfile/dumpfile to . In this part we would look at the injections in the update Query. 
在本系列的早期部分,我们看了一下基于GET和POST注入一头扎进基于SQL注入的错误的详细信息(字符串型,整型),二次查询注入基于错误,盲目注入(布尔型和基于时间)或使用在输出文件/转储文件,以。在这一部分,我们将看看在更新查询的注射。 

A general update query looks like
一般更新查询样子:UPDATE TABLE SET PARAMETER-1="some value" WHERE  PARAMETER-2="some value";

In the general case, a front end for a password update or profile update would have this query working in the backend. During a pentest be extra careful while handling these queries, because one wrong test can update the complete production database with wrong values.
在一般情况下,对于一个密码更新或配置文件更新的前端会有这种查询在后台工作。在一次测试中要格外小心,而处理这些质疑,因为一个错误的测试有更新完整的数据库错误的产生价值。


In the update statement injection, our objective is to extract info and not update the database, therefore we can use the the basics of
在update语句注入,我们的目标是提取信息,并及时更新数据库,因此我们可以使用的基本知识:
    Double query injection
    Blind injection
    Dumpfile/outfile to extract the information.
Basic query injections
Less-17 Line number 97
$update="UPDATE users SET password = ‘$passwd‘ WHERE username=‘$row1‘";




SQLI-LABS SERIES PART-15
INJECTION IN THE INSERT QUERY:插入执行注入
INJECTION IN HEADERS:refer头注入

一般插入查询样子:INSERT INTO table (col1,col2, col3) values (val1,val2, val3);

"Less-18 - INJECTION IN THE USERAGENT FIELD"   
"Less-19 - INJECTION IN THE REFER FIELD."

For the purpose of fuzzing these input points we need to write a script or use interceptor proxies like Tamper data (add on for Firefox), Burp suite, Fiddler, Zap, or any other tool which allows you to modify the headers on the fly.
对于这些模糊测试输入点的目的,我们需要编写一个脚本或使用拦截器代理类数据防篡改(加上用于Firefox),burpsuit,Fiddler,ZAP,或任何其他工具,它允许你修改标题。
These sort of injections where the Header fields are being inserted into the database, our focus is to check if the data can be extracted from it is certain way. Well blind is always an option and we can use Boolean or time based injections. The process works but is overall slow.
这些地方的头字段排序被插入到数据库中注入的,我们的重点是,以检查是否可以数据形式提取它。当然盲目始终是一个选项,我们可以使用布尔或基于时间的注射。该过程的工作,但总体缓慢。
In cases where MySQL errors are displayed by the application, this can be used to dump the values efficiently and with much lesser number of queries as compared to Blind based. The logic of Double query injections is used to dump the info.
在其中显示由应用MySQL的错误的情况下,这可以被用来有效地和以比盲基于查询的小得多的数目倾倒的值。二次查询注射的逻辑是用来转储信息。



SQLI-LABS SERIES PART-16

SQL Injection via COOKIES:cookie注入

As we discussed before in the earlier posts, enumeration is the first and very essential part of every penetration test. Knowing the workflow of application is trivial to its testing. The more one knows about the application, the better and effective it becomes. 
Secondly, all input parameters should be tested. These do not include just the form fields, but also the other input fields like the referrers, user-agent, cookies. Developers these days take care to sanitize the the form inputs and take extra care for those but forget to pay the same attention to the other inputs.

We discussed the injection in update query and injection in the headers in our previous lessons. In  this lesson we will discuss the injection via cookies. 
Technically speaking, it is trivial to understand, how cookies are being generated by application and how the same is used within application. Understanding the cookie generation logic is trivial to the successful testing.
In Less-20, we introduce the basics and use a clear text cookie generation logic to explain. There is no difference in how the injection is performed via a cookie or any other form parameter. In Less-20 and 21 we deal with error based injections.
Less-21 uses an encoded cookie value and the application logic is used to encode and decode the data and use the result in the queries later on.

For Less-21 we observe that the system is using Base64 encoding scheme to send an encoded cookie to the browser. Hence forth we need to encode our injections using Base64 to be consumed nicely by the web application.



SQLI-LABS SERIES PART-17
SECOND ORDER INJECTIONS:二次排序注入

What are second order injection?
Second order injections can be considered simmilar to the stored XSS. Injection does not directly yeild result but this injection is used at some other places insecurely causing the injection to trigger and yield results.
As an Example to understand this imagine an application which allows user to create a user account in the system. Now a malicious user can try to inject SQL keywords in the user creation form which is nicely escaping dangerous characters by use of mysql_real_escape_string() and making it safe for that form. Because mysql_real_escape_string() escapes the dangerous chars, therefore if attacker tries to inject admin OR 1=1--  in the username field, because of the function, the quotes would be escaped and input would become admin\‘ or 1=1--. If other values of form are valid, the user account is created with the name admin OR 1=1-- .

Now with the user created, he can login with newly created account and go to the password reset page. As this page is checking for old password , and building up a query behind the scene something like
UPDATE users SET passwd="New_Pass" WHERE username="Username" and passwd="oldpassword"

Now as the username is being taken from the database, the developer treats this as trusted info. As he is confident that initially there is no SQL Injection on the pages. This blind trust on the data from database is called without any escaping, thereby causing the username to trigger the SQL injection.

The query becomes UPDATE users SET passwd="New_Pass" WHERE username =   admin --   AND password= old password  .
Effectively SQLi working and changing the password of Admin user commenting out rest of query which is marked in brown

 

文章来自:http://www.cnblogs.com/Lzero/p/3978091.html
© 2021 jiaocheng.bubufx.com  联系我们
ICP备案:鲁ICP备09046678号-3