Natas30:
本关是一个登录页面,查看源码,可以发现关键代码。
if ('POST' eq request_method && param('username') && param('password')){ my $dbh = DBI->connect( "DBI:mysql:natas30","natas30", "<censored>", {'RaiseError' => 1}); my $query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password')); my $sth = $dbh->prepare($query); $sth->execute(); my $ver = $sth->fetch(); if ($ver){ print "win!<br>"; print "here is your result:<br>"; print @$ver; } else{ print "fail :("; } $sth->finish(); $dbh->disconnect(); }
问题出在$dbh->quote(param('password'))这里。
在Perl中,可以使用param('name')方法获取post表单中name参数的值,但是这个方法有一个特点,那就是
当我们输入name=foo时,param('name')方法返回的是name的值foo;
当我们输入name=foo&name=bar时,param('name')方法返回的是name的值列表["foo","bar"]。
在Perl中,quote()方法的传参类型可以为列表,如果将列表传递给该方法,则quote()会将其解释为单独的参数。比如
@list = ("a", "b") quote(@list)
相当于
quote("a", "b")
Perl的quote($string)方法被用来“转义”包含在string中的任何特殊字符并增加所需的外部的引号。这里使用quote方法的本意是将用户的输入放在引号中以防止sql注入。但是当quote()可以接收两个参数时,它的用法变为:第一个参数表示将要被quote的数据,第二个参数表示一个SQL数据类型,决定如何quote。如果第二个参数是非字符串类型(如NUMERIC),则quote将传递其第一个参数,而不带任何引号。这就构成了SQL注入的机会。
所以,如果调用quote(param('password')方法,我们可以通过抓包并给password赋两个值的方法来给quote()传入两个参数值。其中第二个参数为1个数字(比如2),使第一个参数值不带任何引号直接注入到SQL中,从而得到flag。
构造POST:username=natas31&password='xxx' or 1=1 &password=2
flag:hay7aecuungiuKaezuathuk9biin0pu1
参考:
https://security.stackexchange.com/questions/175703/is-this-perl-database-connection-vulnerable-to-sql-injection/175872#175872
https://stackoverflow.com/questions/40273267/is-perl-function-dbh-quote-still-secure/40275686#40275686
https://blog.csdn.net/baidu_35297930/article/details/99974886?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task