?
Solved

If you wanted to hack this, how would you do it?

Posted on 2012-08-24
15
Medium Priority
?
631 Views
Last Modified: 2012-09-05
I have a contest online where people can vote. In order to prevent someone from voting for the same person more than once, I wrote this script:

$jorja = "SELECT * FROM ballot_box WHERE voter_id = '{$voter_id}' AND contestant_id = '{$contestant_id}' AND ballot_date = '{$today}'";
      $jorja_query = mysqli_query($cxn, $jorja);
            if(!$jorja_query){
            $error = mysqli_errno($cxn).': '.mysqli_error($cxn);                  
            die($error);
            }
      $jorja_count = mysqli_num_rows($jorja_query);
            if($jorja_count>0){
            header("Location:songwriting_already_voted.php?contestant_id=$contestant_id&first_name=$voter_name");
            }

It's not been until recently where all of sudden I experienced a pandemic of people somehow being able to vote hundreds of times for the same person on the same day and I don't know how they're pulling it off.

Now, one thing I just noticed and that's the absence of an "exit();" at then of my (header: Location:...). Maybe that would explain it.

But I would like some minds greater than my own to look at this and see if there's something obvious that you would change in order to prevent someone from being able to vote multiple times.

Hit me...
0
Comment
Question by:brucegust
  • 4
  • 3
  • 3
  • +3
15 Comments
 
LVL 35

Expert Comment

by:gr8gonzo
ID: 38331465
We'd have to see more code. If you have a URL where this is happening, that would help. Chances are that someone is simply messing with the URLs to modify the information being submitted.

So we'd have to see how $voter_id and $contestant_id and $today are being defined.

Post as much of the related code as you can and we can point out the vulnerabilities and how to patch them.
0
 

Author Comment

by:brucegust
ID: 38331485
OK, here's the code and the url in question is the window you get after logging in and voting. I'm not sure if you would want to go to all that trouble, but I do appreciate your time.

Head out to http://www.countryshowdown.com/songwriting

Here's the code for the window that you get after voting. The problem occurs if you try to vote for the same person twice in a 24 hour period. You should be redirected to a window that say, "Nope!" and that hasn't been happening.

Here's the code:

<?php
include ("../carter.inc");
$cxn = mysqli_connect($host,$user,$password,$database)
or die ("couldn't connect to server");

//first thing you need to do is figure out whether or not today's date coincides with a current contest.

$voter_name=$_GET['first_name'];
$contestant_id = $_GET['contestant_id'];
$today =  date("Y-m-d");

//getting info about the contestant

$contestant = "select * from registration where id = '$_GET[contestant_id]'";
$contestant_query = mysqli_query($cxn, $contestant)
or die("Couldn't execute query.");
$contestant_row = mysqli_fetch_assoc($contestant_query);
extract($contestant_row);
$contestant_id = $id;
$contestant_radio_id = $radio_id;
$contestant_bio=$bio;
$contestant_picture =$glamour_shot;
$contestant_stage_name = $stage_name;
$contestant_writer = $songwriting_contest;

if(empty($contestant_stage_name)){
$display_name = $first_name;
}
else
{
$display_name = $contestant_stage_name;
}

$rest = substr("$display_name", -1);
if($rest=="s"){
$sweet_name = $display_name."'";
}
else
{
$sweet_name = $display_name."'s";
}

$voter_id = $_GET['voter_id'];

$esther = "select * from songwriting_contest where start_date <= '$today' AND end_date >= '$today' ORDER by start_date ASC LIMIT 1";
$esther_query = mysqli_query($cxn, $esther);
      if(!$esther_query){
      $crap=mysqli_errno($cxn).': '.mysqli_error($cxn);
      die($crap);
      }
$esther_count = mysqli_num_rows($esther_query);
if($esther_count>0){
$esther_row = mysqli_fetch_assoc($esther_query);
extract($esther_row);
$contest_end_date = $end_date;

$jorja = "SELECT * FROM ballot_box WHERE voter_id = '{$voter_id}' AND contestant_id = '{$contestant_id}' AND ballot_date = '{$today}'";
      $jorja_query = mysqli_query($cxn, $jorja);
            if(!$jorja_query){
            $error = mysqli_errno($cxn).': '.mysqli_error($cxn);                  
            die($error);
            }
      $jorja_count = mysqli_num_rows($jorja_query);
            if($jorja_count>0){
            header("Location:songwriting_already_voted.php?contestant_id=$contestant_id&first_name=$voter_name");
            exit();
            }
            else
            {
//here we're going to see if there's an entry for this voter and the contestant they're wanting to vote for. If there is, we'll update that record. If there isn't, we'll insert a new one
      $poppy = "select * from ballot_box where voter_id = '{$voter_id}' AND contestant_id = '{$contestant_id}'";
      $poppy_query = mysqli_query($cxn, $poppy)
      or die ("Couldn't execute query.");
      $poppy_count = mysqli_num_rows($poppy_query);
            if($poppy_count>0){
            $poppy_row = mysqli_fetch_assoc($poppy_query);
            extract($poppy_row);
            $ballot_box_id = $id;
            $new_ballot_total = $ballots+1;
            //update the row that has both the contestant's id and the voter id to reflect the fact that there's now another vote
            $mimi = "UPDATE ballot_box SET ballots='$new_ballot_total', ballot_date='$today'
            WHERE id = '$ballot_box_id'";
            $mimi_result = mysqli_query($cxn, $mimi)
            or die ("Couldn't execute query.");
            }
            else
            {
            //there wasn't a record of this voter voting for this contestant, so we insert a new row
            $insert = "insert into ballot_box (contestant_id, voter_id, ballot_date, contest_end_date, ballots)                  
            values ('$contestant_id', '$voter_id', '$today', '$contest_end_date','1')";                  
            $insertexe = mysqli_query($cxn, $insert);                  
                  if(!$insertexe) {                  
                  $error = mysqli_errno($cxn).': '.mysqli_error($cxn);                  
                  die($error);                  
                  }
            }
      }
$chad = "SELECT contestant_id, SUM(ballots) FROM ballot_box where contestant_id = '$contestant_id'";
$chad_query = mysqli_query($cxn, $chad)
or die ("Couldn't execute query.");
$chad_row = mysqli_fetch_assoc($chad_query);
//this is the endif for esther for whether or not there's a       record in the ballot box table      
}

$peanut = "select song_title from registration where id='$contestant_id'";
$peanut_query = mysqli_query($cxn, $peanut)
or die("Couldn't execute query.");
$peanut_row = mysqli_fetch_assoc($peanut_query);
extract($peanut_row);
if($peanut_row['song_title']=="" OR $peanut_row['song_title']==" " OR empty($peanut_row['song_title']))
{
$the_message = "Join me in helping $display_name win the Texaco Country Showdown Songwriting Contest by voting now!";
}
else
{
$the_song_title = stripslashes($peanut_row['song_title']);
$the_message = "Join me in helping $display_name win the Texaco Country Showdown Songwriting Contest by voting for &quot;$the_song_title&quot; now!";
}
?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Texaco Country Showdown | America's Largest Country Music Talent Search | Songwriting Contest Vote</title>
<script language="javascript">AC_FL_RunContent = 0;</script>
<script src="AC_RunActiveContent.js" language="javascript"></script>
<link href="mail_stylesheet.css" rel="stylesheet" type="text/css" />

<script language="JavaScript">
<!--
window.resizeTo(700,525)
-->
</SCRIPT>

<script src="https://connect.facebook.net/en_US/all.js" charset="utf-8">
</script>
<script>
FB.init({appId: '345882868804207', status: true, cookie: true, xfbml: true});
</script>
</head>

<body>
<table border="0" cellspacing="0" cellpadding="0" width=100%>
<tr>
<td>
      <table border="0" cellspacing="0" cellpadding="0" width="500">
      <tr>
      <td colspan="3" align="center">
      <IMG SRC="images/new_vote_header.jpg">&nbsp;<BR>
      </td>
      </tr>
      <tr>
      <td>&nbsp;<BR>
                  <table width="650">
                  <tr>
                  <td>
                  &nbsp;<BR>
                  </td>
                  <td>
                  Thanks, <?php echo stripslashes($voter_name); ?>!
                  <P>
                  <?php echo $display_name; ?> says, "Thank you for your vote!" <?php echo $display_name; ?> now has: <b><?php echo $chad_row['SUM(ballots)']; ?> vote(s).</b>
                  <P>
                  Remember, you can vote for <?php echo $display_name; ?> once per day for the entire contest so make <?php echo $sweet_name ?> profile page your
                  <SCRIPT LANGUAGE="JavaScript">
                  <!-- This script and many more are available free online at -->
                  <!-- The JavaScript Source!! http://javascript.internet.com -->

                  <!-- Begin
                  // If it's Internet Explorer, use automatic link
                  // Be sure to change the "http://www.YourWebSiteHere.com\"
                  // to the URL you want them to bookmark.
                  if (document.all){
                    document.write('<A HREF="javascript:history.go(0);" onClick="this.style.behavior=\'url(#default#homepage)\';this.setHomePage(\'https://www.countryshowdown.com/Texaco/contestant.php?id=<?php echo $contestant_id; ?>\');">');
                    document.write('<font color="black" face="arial" size="2">homepage</font></a>');
                  }

                  // If it's Netscape 6, tell user to drag link onto Home button
                  // Be sure to change the "http://www.YourWebSiteHere.com\"
                  // to the URL you want them to bookmark.
                  else if (document.getElementById){
                    document.write('<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>"><font color="black" face="arial" size="2">homepage</font></a>');
                  }

                  // If it's Netscape 4 or lower, give instructions to set Home Page
                  else if (document.layers){
                    document.write('<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>"><font color="black" face="arial" size="2">homepage</font></a>');
                  }

                  // If it's any other browser, for which I don't know the specifications of home paging, display instructions
                  else {
                    document.write('<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>"><font color="black" face="arial" size="2">homepage</font></a>');
                  }
                  //  End -->
                  </script>
                  and make countryshowdown.com part of your daily routine!
                  </td>
                  </tr>
                  <tr>
                  <td>&nbsp;<BR></td>
                  <td>
                        <table width=100% cellspacing="0" cellpadding="0">
                        <tr>
                        <td>
                        <b>Let all your friends know that you've voted for <?php echo $display_name; ?> by clicking on the button to the right. This will post an announcement on your wall on your Facebook page!</b></td><td>
                        <div align="center"><a href="#" onclick="FB.ui({method: 'feed',name: 'I just voted!', link: 'https://apps.facebook.com/texaco_songwriting/', picture: 'https://www.countryshowdown.com/songwriting/images/facebook_news_feed.jpg', caption: 'Texaco Country Showdown Songwriting Contest', description: 'I just voted for <?php echo $display_name; ?> on the Texaco Country Showdown Songwriting Contest' }); return false;"><IMG SRC="images/news_feed_button.jpg" border="0"></a></div>
                        </td>
                        </tr>
                        </table>
                  </td>
                  </tr>
                  </table>
                  </td>
      </tr>
      <tr>
      <td colspan="3">&nbsp;<BR></td></tr>
      <tr>
      <td class="friend_invite" colspan="3" bgcolor="black">
      <div align="center"><font color="white">
      Click <a href="#" onclick="FB.ui({method: 'apprequests',message: '<?php echo $the_message; ?> '}); return false;"><font color="white">here</font></a> to invite your friends to vote for <?php echo $display_name; ?></font></div>
      </td>
      </tr>
      <table>
</td>
</tr>
</table>

</body>
</html>
0
 
LVL 58

Expert Comment

by:Gary
ID: 38331511
Your query is saying if the following is true
if voter_id = '{$voter_id}' AND contestant_id = '{$contestant_id}'"
then update the ballot count.

Surely you shouldn't be updating it as they have already voted for that person and that update code should be in the else condition where no record exists
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
LVL 35

Assisted Solution

by:gr8gonzo
gr8gonzo earned 600 total points
ID: 38331513
Okay, so I definitely see at least one easy issue. You are trusting the values coming from $_GET:

$voter_name=$_GET['first_name'];
$contestant_id = $_GET['contestant_id'];
$voter_id = $_GET['voter_id'];

There's no reason I can't just copy the URL, change the voter_id number, and call it again with a different voter_id to make it seem as though someone else is voting.

You also leave yourself open to SQL injection by doing this. You should NEVER trust ANYTHING that is inside $_GET or $_POST. Think about this:

1. Your PHP script runs and generates HTML, containing a link that would take the user to:
http://www.something.com/vote.php?voter_id=123&contestant_id=123

2. You -expect- the visitor to simply click on that link and be done with it. But instead, the visitor copies the URL, changes voter_id to 124, and then pastes it into his/her browser.

3. Your script runs again, looking at voter_id 124 and contestant_id 123. Well, it looks like voter_id 124 hasn't voted yet, so make it count! Presto, one person has voted twice. And they can keep changing that ID to keep voting over and over again.

The whole web page interaction is a big back-and-forth, which means that the visitor has control over the values at some point, and during that time, they can change them.

So you need to do two things:

1. Change:

$contestant_id = $_GET['contestant_id'];
$voter_id = $_GET['voter_id'];

to


$contestant_id = intval($_GET['contestant_id']);
$voter_id = intval($_GET['voter_id']);

That will make sure that the IDs are actually numbers (so a hacker doesn't try to put in any extra "bad" code instead of an ID).

Then add some kind of unique hash to the link to make sure that people can't tamper with the URL. IF you show us the code that generates the link to vote, we can suggest a way to do this.
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 38331559
0
 
LVL 35

Expert Comment

by:gr8gonzo
ID: 38331568
It gives me a warm, fuzzy feeling everytime you say "what gr8gonzo said."
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 38331588
JH: It gives me a warm, fuzzy feeling when I see the thoughtful work of my respected colleagues published here at EE.   I have learned a lot from you and I am grateful.  All my best, ~Ray
0
 
LVL 4

Assisted Solution

by:TI2Heaven
TI2Heaven earned 200 total points
ID: 38332904
I believe that everybody here misses the point that browsing internet is an anonymous act, so without a credential prompt there is no way to tell which clients are previous voters.
Cookies and cached requests can be erased to make a clean new browse.
Some people share the same ip and source ports changes in each connection.
0
 
LVL 34

Assisted Solution

by:Slick812
Slick812 earned 800 total points
ID: 38333362
greetings  brucegust, , I will agree that you absolutely MUST take steps to avoid Query SQL injection with UN-checked GET values and not using some Safety measure like the mysql_real_escape_string( ) function.

But I'll talk about a way to make your GET string value for voter_id be more difficult to understand and change to "Pack" the ballot box with false votes, this could work for any Number (Integer) value in a GET URL address string.

I'll use the URL converter function  base64_encode( ), and a CRC hash check with function crc32( ). . To hide the value I'll use a simple rotate as the str_rot13( ) function.

    Below is the PHP code to make a base64
$num = 19;// highest num is 99,999,999
$Input = ''.$num; // change the number to a string
$Input =str_pad($Input, 8, '0', STR_PAD_LEFT);// get a consistent number string length
$crc = crc32($Input); // check the original value with a CRC
$crc = chr($crc&255).chr(($crc>>8)&255).chr(($crc>>16)&255).chr($crc>>24);// convert number to string
$Input .= $crc;//crc32($Input);// string is always 12 long
$Input = str_rot13(base64_encode($Input));
$Input = urlencode($Input);
// this $Input is the string value used used for voter_id -
//  as in  www.yoursite.com?voter_id=AQx5BGx5BGy7ttVu
echo 'Number EN is= ',$Input,'<br />';// echo for testing

Open in new window


    The code below is used to Read the base 64 string from above
$Input = $_GET['voter_id'];
$Input = base64_decode(str_rot13($Input));
$crc = substr($Input ,8);
$crc = ord($crc{0})|(ord($crc{1})<<8)|(ord($crc{2})<<16)|(ord($crc{3})<<24);
$Input = substr($Input ,0,8);
if ($crc == crc32($Input)) {
	$Input = (int)$Input;
	echo ' |Number DE is= ',$Input,'<br />';
	} else echo 'ERROR BAD INPUT<br />';// IMPORTANT ERROR means that someone tried to Change vID to stuff ballot box

Open in new window


This is not code for your php code above, but just to show a way to hide and check the number value for an ID, there are other ways to do this, but this seems like some thing you might could do.

Ask questions if you need more info or some explanations about comment
0
 

Author Comment

by:brucegust
ID: 38333482
All of this stuff is great and I do appreciate it.

In other versions of this contest, I've used a session variable that retrieved the contestant id as opposed to it being something that was the product of a "GET" situation.

But here's somethign that I wanted y'all to ponder. While the GET variables can be manually manipulated, what's going into the database are the very same variables. So, if you were to change the URL, you would be changing what's being inputted and that's not the case. I'm seeing multiple rows of the same entries, so the GET variables aren't being manipulated.

That being the case, how would you pull it off if you weren't changing the "GET" stuff?
0
 
LVL 111

Assisted Solution

by:Ray Paseur
Ray Paseur earned 400 total points
ID: 38334078
So, if you were to change the URL, you would be changing what's being inputted and that's not the case. I'm seeing multiple rows of the same entries, so the GET variables aren't being manipulated.
That would seem to imply that some of the programmatic filters are not working correctly.  Maybe a date check is not right?

One of the things that jumps out at me is the use of extract() after a query that used SELECT *.  This has the effect of injecting variables into your scope.  If you have already got a variable of a given name, you may overwrite that variable.
http://php.net/manual/en/function.extract.php

Since we cannot see the data base tables, we cannot really tell you whether there is a risk of variable name collision at the time of extract().  But if you have any column names in any tables that are the same as column names in any other tables, the combination of SELECT * and extract() will usually mean that catastrophe is not left to chance.

Example: The first query uses SELECT * FROM table_1 and thereby selects among other things an AUTO_INCREMENT key named "id".  The second query uses the "id" value in SELECT * FROM table_2 WHERE table_1_id = $id , that finds another row with an AUTO_INCREMENT key named "id", etc.  When extract() is applied to these rows, the value of "id" gets overwritten.  The potential for confusion is unlimited.

Here is the code in the code snippet, in case anyone is interested in working through it.

<?php
include ("../carter.inc");
$cxn = mysqli_connect($host,$user,$password,$database)
or die ("couldn't connect to server");

//first thing you need to do is figure out whether or not today's date coincides with a current contest.

$voter_name=$_GET['first_name'];
$contestant_id = $_GET['contestant_id'];
$today =  date("Y-m-d");

//getting info about the contestant

$contestant = "select * from registration where id = '$_GET[contestant_id]'";
$contestant_query = mysqli_query($cxn, $contestant)
or die("Couldn't execute query.");
$contestant_row = mysqli_fetch_assoc($contestant_query);
extract($contestant_row);
$contestant_id = $id;
$contestant_radio_id = $radio_id;
$contestant_bio=$bio;
$contestant_picture =$glamour_shot;
$contestant_stage_name = $stage_name;
$contestant_writer = $songwriting_contest;

if(empty($contestant_stage_name)){
$display_name = $first_name;
}
else
{
$display_name = $contestant_stage_name;
}

$rest = substr("$display_name", -1);
if($rest=="s"){
$sweet_name = $display_name."'";
}
else
{
$sweet_name = $display_name."'s";
}

$voter_id = $_GET['voter_id'];

$esther = "select * from songwriting_contest where start_date <= '$today' AND end_date >= '$today' ORDER by start_date ASC LIMIT 1";
$esther_query = mysqli_query($cxn, $esther);
      if(!$esther_query){
      $crap=mysqli_errno($cxn).': '.mysqli_error($cxn);
      die($crap);
      }
$esther_count = mysqli_num_rows($esther_query);
if($esther_count>0){
$esther_row = mysqli_fetch_assoc($esther_query);
extract($esther_row);
$contest_end_date = $end_date;

$jorja = "SELECT * FROM ballot_box WHERE voter_id = '{$voter_id}' AND contestant_id = '{$contestant_id}' AND ballot_date = '{$today}'";
      $jorja_query = mysqli_query($cxn, $jorja);
            if(!$jorja_query){
            $error = mysqli_errno($cxn).': '.mysqli_error($cxn);                  
            die($error);
            }
      $jorja_count = mysqli_num_rows($jorja_query);
            if($jorja_count>0){
            header("Location:songwriting_already_voted.php?contestant_id=$contestant_id&first_name=$voter_name");
            exit();
            }
            else
            {
//here we're going to see if there's an entry for this voter and the contestant they're wanting to vote for. If there is, we'll update that record. If there isn't, we'll insert a new one
      $poppy = "select * from ballot_box where voter_id = '{$voter_id}' AND contestant_id = '{$contestant_id}'";
      $poppy_query = mysqli_query($cxn, $poppy)
      or die ("Couldn't execute query.");
      $poppy_count = mysqli_num_rows($poppy_query);
            if($poppy_count>0){
            $poppy_row = mysqli_fetch_assoc($poppy_query);
            extract($poppy_row);
            $ballot_box_id = $id;
            $new_ballot_total = $ballots+1;
            //update the row that has both the contestant's id and the voter id to reflect the fact that there's now another vote
            $mimi = "UPDATE ballot_box SET ballots='$new_ballot_total', ballot_date='$today'
            WHERE id = '$ballot_box_id'";
            $mimi_result = mysqli_query($cxn, $mimi)
            or die ("Couldn't execute query.");
            }
            else
            {
            //there wasn't a record of this voter voting for this contestant, so we insert a new row
            $insert = "insert into ballot_box (contestant_id, voter_id, ballot_date, contest_end_date, ballots)                  
            values ('$contestant_id', '$voter_id', '$today', '$contest_end_date','1')";                  
            $insertexe = mysqli_query($cxn, $insert);                  
                  if(!$insertexe) {                  
                  $error = mysqli_errno($cxn).': '.mysqli_error($cxn);                  
                  die($error);                  
                  }
            }
      }
$chad = "SELECT contestant_id, SUM(ballots) FROM ballot_box where contestant_id = '$contestant_id'";
$chad_query = mysqli_query($cxn, $chad)
or die ("Couldn't execute query.");
$chad_row = mysqli_fetch_assoc($chad_query);
//this is the endif for esther for whether or not there's a       record in the ballot box table      
}

$peanut = "select song_title from registration where id='$contestant_id'";
$peanut_query = mysqli_query($cxn, $peanut)
or die("Couldn't execute query.");
$peanut_row = mysqli_fetch_assoc($peanut_query);
extract($peanut_row);
if($peanut_row['song_title']=="" OR $peanut_row['song_title']==" " OR empty($peanut_row['song_title']))
{
$the_message = "Join me in helping $display_name win the Texaco Country Showdown Songwriting Contest by voting now!";
}
else
{
$the_song_title = stripslashes($peanut_row['song_title']);
$the_message = "Join me in helping $display_name win the Texaco Country Showdown Songwriting Contest by voting for &quot;$the_song_title&quot; now!";
}
?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Texaco Country Showdown | America's Largest Country Music Talent Search | Songwriting Contest Vote</title>
<script language="javascript">AC_FL_RunContent = 0;</script>
<script src="AC_RunActiveContent.js" language="javascript"></script>
<link href="mail_stylesheet.css" rel="stylesheet" type="text/css" />

<script language="JavaScript">
<!--
window.resizeTo(700,525)
-->
</SCRIPT>

<script src="https://connect.facebook.net/en_US/all.js" charset="utf-8">
</script>
<script>
FB.init({appId: '345882868804207', status: true, cookie: true, xfbml: true});
</script>
</head>

<body>
<table border="0" cellspacing="0" cellpadding="0" width=100%>
<tr>
<td>
      <table border="0" cellspacing="0" cellpadding="0" width="500">
      <tr>
      <td colspan="3" align="center">
      <IMG SRC="images/new_vote_header.jpg">&nbsp;<BR>
      </td>
      </tr>
      <tr>
      <td>&nbsp;<BR>
                  <table width="650">
                  <tr>
                  <td>
                  &nbsp;<BR>
                  </td>
                  <td>
                  Thanks, <?php echo stripslashes($voter_name); ?>!
                  <P>
                  <?php echo $display_name; ?> says, "Thank you for your vote!" <?php echo $display_name; ?> now has: <b><?php echo $chad_row['SUM(ballots)']; ?> vote(s).</b>
                  <P>
                  Remember, you can vote for <?php echo $display_name; ?> once per day for the entire contest so make <?php echo $sweet_name ?> profile page your
                  <SCRIPT LANGUAGE="JavaScript">
                  <!-- This script and many more are available free online at -->
                  <!-- The JavaScript Source!! http://javascript.internet.com -->

                  <!-- Begin
                  // If it's Internet Explorer, use automatic link
                  // Be sure to change the "http://www.YourWebSiteHere.com\"
                  // to the URL you want them to bookmark.
                  if (document.all){
                    document.write('<A HREF="javascript:history.go(0);" onClick="this.style.behavior=\'url(#default#homepage)\';this.setHomePage(\'https://www.countryshowdown.com/Texaco/contestant.php?id=<?php echo $contestant_id; ?>\');">');
                    document.write('<font color="black" face="arial" size="2">homepage</font></a>');
                  }

                  // If it's Netscape 6, tell user to drag link onto Home button
                  // Be sure to change the "http://www.YourWebSiteHere.com\"
                  // to the URL you want them to bookmark.
                  else if (document.getElementById){
                    document.write('<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>"><font color="black" face="arial" size="2">homepage</font></a>');
                  }

                  // If it's Netscape 4 or lower, give instructions to set Home Page
                  else if (document.layers){
                    document.write('<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>"><font color="black" face="arial" size="2">homepage</font></a>');
                  }

                  // If it's any other browser, for which I don't know the specifications of home paging, display instructions
                  else {
                    document.write('<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>"><font color="black" face="arial" size="2">homepage</font></a>');
                  }
                  //  End -->
                  </script>
                  and make countryshowdown.com part of your daily routine!
                  </td>
                  </tr>
                  <tr>
                  <td>&nbsp;<BR></td>
                  <td>
                        <table width=100% cellspacing="0" cellpadding="0">
                        <tr>
                        <td>
                        <b>Let all your friends know that you've voted for <?php echo $display_name; ?> by clicking on the button to the right. This will post an announcement on your wall on your Facebook page!</b></td><td>
                        <div align="center"><a href="#" onclick="FB.ui({method: 'feed',name: 'I just voted!', link: 'https://apps.facebook.com/texaco_songwriting/', picture: 'https://www.countryshowdown.com/songwriting/images/facebook_news_feed.jpg', caption: 'Texaco Country Showdown Songwriting Contest', description: 'I just voted for <?php echo $display_name; ?> on the Texaco Country Showdown Songwriting Contest' }); return false;"><IMG SRC="images/news_feed_button.jpg" border="0"></a></div>
                        </td>
                        </tr>
                        </table>
                  </td>
                  </tr>
                  </table>
                  </td>
      </tr>
      <tr>
      <td colspan="3">&nbsp;<BR></td></tr>
      <tr>
      <td class="friend_invite" colspan="3" bgcolor="black">
      <div align="center"><font color="white">
      Click <a href="#" onclick="FB.ui({method: 'apprequests',message: '<?php echo $the_message; ?> '}); return false;"><font color="white">here</font></a> to invite your friends to vote for <?php echo $display_name; ?></font></div>
      </td>
      </tr>
      <table>
</td>
</tr>
</table>

</body>
</html>

Open in new window

0
 
LVL 35

Expert Comment

by:gr8gonzo
ID: 38334110
1. If you have the EXACT same data in your row, then that does point to a logic problem somewhere. Something you SHOULD do that will at least fix the symptom would be to add a unique index on the database table so that the combination of voter_id, contestant_id, and ballot_date is unique.

This will at least enforce the limit on the database level so that any programming problems will not allow multiple votes for someone for a specific candidate on the same day. It doesn't fix the original problem, but it should definitely be a measure to take.

You'll need to dedupe your existing ballot_box table first, though....

... and I just went back to look at the tables involved and I see that the ballot_box table has AT LEAST these columns:

contestant_id, voter_id, ballot_date, contest_end_date, ballots

I don't understand your database structure here. Let's say that one "vote" results in a unique combination of contestant_id, voter_id, and ballot_date. That becomes one record, right? Why would you have a "ballots" field containing a sum of ballots? I think you are trying to combine two concepts into one table.

I would honestly just drop the "ballots" field altogether. Unless you have millions of votes and you are regularly displaying the overall ballot counts to every visitor, there is no need to try to store the total count and try to update it. You can just do something like:

SELECT contestant_id, COUNT(contestant_id) as ballots FROM ballot_box WHERE contest_end_date=<whatever this value is> GROUP BY contestant_id;

Open in new window


If each record in the ballot_box table reflects a legitimate, single ballot, then this should result in:

contestant_id   |  ballots
1                        |  14
2                         |  8
3                         | 63

Open in new window


...etc....

If your ballot_box_table only has those 5 columns that I mentioned, then you can run this code (make a backup first, as usual) to de-dupe your ballot_box table. (Modify the first line so that the structure matches ballot_box)

CREATE TABLE `ballot_box_tmp` (  `contestant_id` INT(10) NULL,  `voter_id` INT(10) NULL,  `end_date` VARCHAR(50) NULL,  `ballot_date` VARCHAR(50) NULL,  `ballots` INT NULL ) COLLATE='latin1_swedish_ci' ENGINE=MyISAM ROW_FORMAT=DEFAULT;

INSERT INTO ballot_box_tmp (contestant_id,voter_id,end_date,ballot_date,ballots)
SELECT DISTINCT contestant_id,voter_id,end_date,ballot_date,1
FROM ballot_box;

TRUNCATE TABLE ballot_box;

INSERT INTO ballot_box (contestant_id,voter_id,end_date,ballot_date,ballots)
SELECT DISTINCT contestant_id,voter_id,end_date,ballot_date,1
FROM ballot_box_tmp;

DROP TABLE ballot_box_tmp;

Open in new window


Once de-duped, add the unique index on those three fields (contestant_id,voter_id, and ballot_date) to make sure you don't get multiple ballots for the same combinations.
0
 
LVL 34

Accepted Solution

by:
Slick812 earned 800 total points
ID: 38337923
OK, I tried to look at your code again, and I see so many places where the code "May Not" be doing what you want it to do, as far as correctly "adding to" and counting the votes (ballots) for a specific candidate, and some how preventing multiple votes form the same user. I can not really tell from just looking at the code (there's so much of it) if your setup does this or not, But - since you say it works, and is somehow "Hacked", I guess that it does do that.
I really can not get past your use of code that is absolutely unprotected from SQL injection, like this line -
$contestant = "select * from registration where id = '$_GET[contestant_id]'";

I do not mean to say that your problem if from malicious SQL injection, but it could happen, with code like that.

Something that I do not get is this -

$poppy_count = mysqli_num_rows($poppy_query);
    if($poppy_count>0){
// then you proceed to UPDATE (increase) the ballots to $new_ballot_total
// EVEN THOUGH the query found that row already existed
    $mimi = "UPDATE ballot_box SET ballots='$new_ballot_total', ballot_date='$today'
    }

It does not seem like this would prevent multiple votes, but I may NOT be looking at other factors that would affect this to prevent that?



Also, this looks like you may have copied the source code for this from somewhere, and it was OLD outdated code, as I see this line -

 // If it's Netscape 4 or lower, give instructions to set Home Page
    else if (document.layers){

at this point in time you really do not need to consider Netscape 4 browser, but even if you do include that code, you have the same exact "Home Page" code for ALL of the IF statements as -
'<a href="homepage_instructions.php?contestant_id=<?php echo $contestant_id; ?>">

you should only use IF statements when Different output is need for "if is true" and something else for the non - if .

I guess that my main suggestion would be to start another PHP code workup for that page, that uses some protections for GET input checking and mySQL injection guards, and use a different QUERY setup to access, check, and update the voting process. I don't have any code for that now, you have already been given several suggestions, but, this voting and checking vote dates and amounts is a common design setup.
0
 
LVL 4

Expert Comment

by:TI2Heaven
ID: 38349713
hello?.
0
 

Author Comment

by:brucegust
ID: 38369055
After digesting all of the counsel that was given, I was able to go back and figure out, not only where it was that my rascals were able to exploit the weaknesses in my code, but also learned some other things along the way.

The common denominator in the information that was being entered into the database was a valid contestant id, a vote and the supposed facebook id.

Since the facebook id is the number of a profile that I should be able to view in Facebook, I was surprised to find that no profile came up.

The facebook id was 2147483647. When I did a google search on that number, I was pleased to find out that this is the number that is entered into a database when the number exceeds the limit of digits allowed in that database field.

The thing that made it look innocent is the fact that it has the same number of digits as a facebook id.

So I went back to the vote page and noticed how I could change the voter id in the URL by simply adding an exorbitant number of digits. As long as I kept the number of digits more that what would be allowed in the database, the insert statement would always have 2147483647 as the voter id. And, because I was putting in a different number, my safeguards were fooled into thinking it was a different person.

The reason this hasn't happened in the past is because I had a session variable at the top of my voting page which was used to define the voter id, rather than it being a variable in the URL.

So what I'm going to do is establish a session variable based on the user's facebook id and that will be the criteria used in my safeguards to ensure that there isn't foul play afoot.

Feels good to figure it out! Thanks for all the input, especially those who obviously took some time to craft their response!
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I imagine that there are some, like me, who require a way of getting currency exchange rates for implementation in web project from time to time, so I thought I would share a solution that I have developed for this purpose. It turns out that Yaho…
Build an array called $myWeek which will hold the array elements Today, Yesterday and then builds up the rest of the week by the name of the day going back 1 week.   (CODE) (CODE) Then you just need to pass your date to the function. If i…
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
Suggested Courses
Course of the Month13 days, 9 hours left to enroll

749 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question