Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re^2: using Safe.pm

by Aldebaran (Curate)
on Jul 07, 2015 at 07:13 UTC ( [id://1133507]=note: print w/replies, xml ) Need Help??


in reply to Re: using Safe.pm
in thread using Safe.pm

An interesting challenge. I've been fiddling with it for 48 hours and decided that altering the windows/widgets was beyond my skill level. Since the output goes to STDOUT, I opted to create an event that would do so. I sincerely hope the readmore tags work this time:

#!perl -w # sigeval.pl # This application demonstrates how to put a basic Perl/Tk application # together. use strict; use Tk 800.000; # These are all the modules that we are using in this script. use Tk::Frame; use Tk::TextUndo; use Tk::Text; use Tk::Scrollbar; use Tk::Menu; use Tk::Menubutton; use Tk::Adjuster; use Tk::DialogBox; # Main Window my $mw = new MainWindow; $mw->geometry('400x300'); # We need to split our application into three frames: # 1. A widget to contain a list of files from the current directory # 2. A widget that we can load a text file into, or copy/paste text i +nto # 3. A widget to display the output of our Perl code created by # 'eval'ing the Perl code in the top text widget. # Frames # The Adjuster provides a splitter between the frames on the left and # the right so we can resize the frames vertically my $lf = $mw->Frame; # Left Frame; my $aj = $mw->Adjuster(-widget => $lf, -side => 'left'); my $rf = $mw->Frame; # Right Frame; # Menu Bar # This is the Tk 800.00 way to create a menu bar. The # menubar_menuitems() method returns an anonymous array containing all # the information that is needed to create a menu. my $mb = $mw->Menu(-menuitems => &menubar_menuitems() ); # The configure command tells the main window to use this menubar; # several menubars could be created and swapped in and out, if you # wanted to. $mw->configure(-menu => $mb); # Use the "Scrolled" Method to create widgets with scrollbars. # The listbox is our filename container. my($ListBox) = $lf->Scrolled('Listbox', -height => '0', -width => '0', -scrollbars => 'e', ); # The default key-bindings for the Text widgets and its derivatives # TextUndo, and ROText are emacs-ish, e.g. ctrl-a cursor to beginning # of line, ctrl-e, cursor to end of line, etc. # The 'o' in 'osoe' means optionally, so when the widget fills up, the # scrollbar will appear, otherwise we are binding the scrollbars to # the 'south' side and to the 'east' side of the frame. my($InputText) = $rf->Scrolled('TextUndo', -height => '1', -width => '1', -scrollbars => 'osoe', ); # We use the 'Text' widget here because we do not need to edit # anything in the widget. We could have used 'ROText' here as well # (Read Only Text Widget). my($OutputText) = $rf->Scrolled('Text', -height => '1', -width => '1', -scrollbars => 'osoe', ); # Load filenames into the listbox. opendir DIR, "."; $ListBox->insert('end', grep { -f $_ } readdir DIR); close DIR; # Binding subs to events # Every widget that is created in the Perl/Tk application either # creates events or reacts to events. # Callbacks are subs that are used to react to events. A callback is # nothing more than a sub that is bound to a widget. # The most common ways to bind a sub to an event are by using an # anonymous sub with a call to your method inside it, such as in the # following 'Key' bindings, or with a reference to the callback sub, # as in the 'ButtonRelease' binding. # Left mouse button loads file and eval's if .pl suffix. See the # OnLoad sub for more details. $ListBox->bind('<ButtonRelease-1>', [\&OnLoad] ); # CTRL-L, eval text widget contents $mw->bind('Tk::TextUndo', '<Control-Key-l>', sub { OnEval(); } ); # CTRL-O, load a text file into the text widget $mw->bind('Tk::TextUndo', '<Control-Key-o>', sub { OnFileOpen(); } ); # CTRL-S, save text as with file dialog $mw->bind('Tk::TextUndo', '<Control-Key-s>', sub { OnFileSave(); } ); # CTRL-Q, quit this application $mw->bind('Tk::TextUndo', '<Control-Key-q>', sub { OnExit(); } ); # CTRL-D, deparse this application $mw->bind('<Control-Key-d>', sub { OnDeparse(); } ); # Pack everything # IMPORTANT: if you don't pack it, it probably won't show the way you # want it to, or even not show up at all! # some things to try: # 1. change the order of $lf, $aj, and $rf # 2. add -expand 1 to ListBox # 3. comment out this section so widgets are not packed $lf->pack(qw/-side left -fill y/); $aj->pack(qw/-side left -fill y/); $rf->pack(qw/-side right -fill both -expand 1/); $ListBox ->pack(qw/-side left -fill both -expand 1/); $InputText ->pack(qw/-side top -fill both -expand 1/); $OutputText->pack(qw/-side bottom -fill both -expand 1/); # Start the main event loop MainLoop; exit 0; # return an anonymous list of lists describing the menubar menu items sub menubar_menuitems { return [ map ['cascade', $_->[0], -tearoff=> 0, -menuitems => $_->[1]], # make sure you put the parens here because we want to # evaluate and not just store a reference ['~File', &file_menuitems()], ['~Help', &help_menuitems()], ]; } sub file_menuitems { # 'command', tells the menubar that this is not a label for a sub # menu, but a binding to a callback; the alternate here is 'cascade' # Try uncommenting the following code to create an 'Operations' sub # menu in the main 'File' menu. return [ # [qw/cascade Operations -tearoff 0 -menuitems/ => # [ # [qw/command ~Open -accelerator Ctrl-o/, # -command=>[\&OnFileOpen]], # [qw/command ~Save -accelerator Ctrl-s/, # -command=>[\&OnFileSave]], # ] # ], [qw/command ~Open -accelerator Ctrl-o/, -command=>[\&OnFileOpen]], [qw/command ~Save -accelerator Ctrl-s/, -command=>[\&OnFileSave]], '', [qw/command E~xit -accelerator Ctrl-q/, -command=>[\&OnExit]], ]; } sub help_menuitems { return [ ['command', 'About', -command => [\&OnAbout]] ]; } # Here is our "Exit The Application" callback method. :-) sub OnExit { exit 0; } # The TextUndo widget has a file load dialog box method built-in! sub OnFileOpen { $InputText->FileLoadPopup(); } # The TextUndo widget has a file save dialog box method built-in! sub OnFileSave { $InputText->FileSaveAsPopup(); # refresh the list box &LoadListBox(); } sub LoadListBox { # Remove current contents otherwise we would just append the # filenames to the end, and this is not what we want. $ListBox->delete('0.1', 'end'); # Just use a plain old grep readdir pipeline to create a list of # filenames for our listbox. opendir DIR, "."; $ListBox->insert('end', grep { -f $_ && -r $_ } readdir DIR); close DIR; } # Show the Help->About Dialog Box sub OnAbout { # Construct the DialogBox my $about = $mw->DialogBox( -title=>"About Jack", -buttons=>["OK"] ); # Now we need to add a Label widget so we can show some text. The # DialogBox is essentially an empty frame with no widgets in it. # You can images, buttons, text widgets, listboxes, etc. $about->add('Label', -anchor => 'w', -justify => 'left', -text => qq( Perl Eval-uator v1.0 by David Hisel -Click on a filename to view it, and if it has a ".pl" suffix, it will be evaluated automatically, or -Copy and paste Perl code to the top window, then -Hit CTRL-L to evaluate the code and display the output in the bottom text widget. ) )->pack; $about->Show(); } # Load a file into the $InputText widget sub OnLoad { # Getting the text of the selected item in a listbox is a two step # process, first you get the index and then, using the index, my ($index) = $ListBox->curselection(); # fetch the contents from the listbox. my $filename = $ListBox->get($index); # TextUndo widget has a built-in Load sub! $InputText->Load( $filename ); # we need to make sure we don't eval ourself otherwise we crash (my $script = $0) =~ s,.*(\/|\\),,; # If it ends in ".pl" automatically eval the code &OnEval() if $filename =~ /\.pl$/ && $filename !~ /$script/; } #evaluates code in the entry text pane sub OnEval{ # The Text widget has a TIEHANDLE module implemented so that you # can tie the text widget to STDOUT for print and printf; note, if # you used the "Scrolled" method to create your text widget, you # will have to get a reference to it and pass that to "tie", # otherwise it won't work. my $widget = $OutputText->Subwidget("text"); tie *STDOUT, ref $widget, $widget; # need "no strict;" otherwise we can't run obfu nor other japh's eval ("no strict;".$InputText->get(0.1, 'end')); # be polite and output an error if something goes wrong. print "ERROR:$@" if $@; print "\n"; } sub OnDeparse{ # The Text widget has a TIEHANDLE module implemented so that you # can tie the text widget to STDOUT for print and printf; note, if # you used the "Scrolled" method to create your text widget, you # will have to get a reference to it and pass that to "tie", # otherwise it won't work. my $widget = $OutputText->Subwidget("text"); tie *STDOUT, ref $widget, $widget; # need "no strict;" otherwise we can't run obfu nor other japh's print "before eval\n"; #eval ("no strict;".$InputText->get(0.1, 'end')); # be polite and output an error if something goes wrong. print "ERROR:$@" if $@; print "\n"; }

When I depress ctrl-d, I get to the sub that prints "before eval" to STDOUT. My current problem is writing the appropriate eval statement, in other words, trying to imitate the command line from within a program. My best guess did not work:

#!/usr/bin/perl use Modern::Perl qw/2010/; use Safe; use File::Slurp; my $compartment = Safe->new(); my $japh = read_file( 'obfu3.pl' ) ; my $result = $compartment->reval($japh); say "Result is $result"; eval ('perl -MO=Deparse obfu3.pl');

The top part works; the bottom doesn't. Looking for tips. Thank you.

Replies are listed 'Best First'.
Re^3: using Safe.pm
by CountZero (Bishop) on Jul 07, 2015 at 12:00 UTC
    You don't have to start an external Perl instance. B::Deparse works equally well from within your script:
    use B::Deparse; my $deparse = B::Deparse->new(); my $body = $deparse->coderef2text(sub { # your program here }); print $body;

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
Re^3: using Safe.pm ( Safe::Deparse )
by Anonymous Monk on Jul 07, 2015 at 07:59 UTC

      Thanks for your response, I chased down those cpan modules and gave them a read. It's been suggested that I might not have my 'puter at eye level, and the answers might be right there on the screen, but I'm missing them usually for lack of comprehension. This is new territory for me, and the learning curve is steep. I do best with the modules that have examples and have worked up a few examples to show what is working and what isn't. I wrote the simplest of toy programs: toy1.pl, that only substitutes a phrase. It's safe and short. Let me list the latest of many incarnations of this script:

      #!/usr/bin/perl use warnings; use strict; use feature 'say'; use File::Slurp; use B::Deparse; # first trial without safe my $japh = read_file( 'toy1.pl' ) ; say "japh is $japh"; eval($japh); # second trial with safe and copy/paste my $deparse = B::Deparse->new(); my $body = $deparse->coderef2text(sub { my $name = 'Donald Trump'; $name =~ s/ / "in the sub" /; say "name is $name"; }); say "second trial is $body"; # third trial combines two methods my $deparse2 = B::Deparse->new(); my $body2 = $deparse2->coderef2text(sub { my $japh2 = read_file( 'toy1.pl' ) ; say "japh2 is $japh2"; eval($japh2); }); say "third trial is $body2"; # 4th trial uses backticks my $file = 'toy1.pl'; my $command = "perl -MO=Deparse $file"; say "command is $command"; my $output = `$command`; say "4th trial is $output"; # 5th trial looks at original obfu $file = 'obfu3.pl'; $command = "perl -MO=Deparse $file"; $output = `$command`; say "5th trial is $output";

      The problem seems to be how to get deparse to work when the file to be deparsed is being selected after the program begins to run. Hence copying and pasting into a sub is not possible. I thought I was going to go with system() and Capture::Tiny until I ended up going with backticks in the 4th trial with good results. On the fifth trial I directed the backend of perl toward the obfu in the thread of the obfu decoder ring from the original post. It appears to be a bunch of junk under closer inspection:

      japh is #!/usr/bin/perl use Modern::Perl qw/2010/; my $name = 'Donald Trump'; $name =~ s/ / "Perl is great" /; say "name is $name"; name is Donald "Perl is great" Trump second trial ia { use warnings; use strict; use feature 'say'; my $name = 'Donald Trump'; $name =~ s/ / "in the sub" /; say "name is $name"; } third trial is { use warnings; use strict; use feature 'say'; my $japh2 = read_file('toy1.pl'); say "japh2 is $japh2"; eval $japh2; } command is perl -MO=Deparse toy1.pl toy1.pl syntax OK 4th trial is use Modern::Perl ('2010'); use warnings; use strict; use feature 'say', 'state', 'switch'; my $name = 'Donald Trump'; $name =~ s/ / "Perl is great" /; say "name is $name"; obfu3.pl syntax OK 5th trial is $= = $'; $. | $| unless $; $_ = '*$(^@(%_+&~~;#~~/.~~;_);;.);;#);~~~~;_,.~~,.*+,./|~~;_);@-,.;.); +~~,./@@-__ );;.);;#,.;.~~@-);;#);;;);~~,.*+,.;#);;;;#-(@-__);;.);;#,.;.~~@-););,. +/.);~~,./| ,.*+,./|,.););;#;#-(@-__);;.);;#,.;.~~@-;;,.,.*+,./@,.;.;#__;#__;;,.,. +*+,./|,.;; ;#-(@-__@-__,.;_);@-,.;.,./|~~();.;#;.;;;;;;;;;.;.~~;.~~~~/@~~@-~~~~;# +/|;#/|~~~~ ~~/@~~@-~~~~~~;_,.;;,.;.);,.~~;_,./|);;.,./@,./@~~~~~~*+;#-(@-__,.,.,. +*+,./|,.;; ~~()~~@-);;#);;.,.~~~~@-);-(@-__@-*+);~~,..%,.;;,.*+);~~~~@-,.*+,.,.~~ +@-~~.%,.;; ~~@-,./.,./|,.;;~~@-~~.%););;#-(@-__@-*+);;.,./|,./@,.*+,./|,.-(~~@-,. +*+,.,.~~@- ~~.%,.,.~~@-,./.,./|,.;;~~@-~~.%););;#-(@-__);.%~~/@~~@-~~~~~~;_,.(),. +;_,..%,.;. ~~;_~~;;;#/|~~~~~~*+;#-(@-__);@-);~~,.*+,./|);;;~~@-~~~~;;__;;/.;.@-;; +();./@,./| ~~~~;#-(@-__&$#%^'; $__ = ' '; use arybase (); $___ = '````' | "$[`$[" | '`%",'; $~ = ("$___$__-$[``$__" | "$___" | "$___$__-$[.%") . (q['`] | "'$[" | +q['#]) . ' /.*?&([^&]*)&.*/$' . ++$= . ('/``' | "/$[`" | q[/#']) . (";`/[\\`\\`$_ +_]//`;" | ";$[/[\\$[\\`$__]//`;" | ";#/[\\\$\\.$__]//'") . '@:=("@-","/.","~~"," +;#",";;"," ;.",",.",");","()","*+","__","-(","/@",".%","/|",";_");@:{@:}=$%..$#:; +' . ('`' | "$[" | '#') . '/(..)(..)/' . ('```' | "``$[" | '#("') . '(($:{$' . $= + . '}<<' . (++$= + $=) . ')|($:{$' . $= . '}))/' . ('```;' | "``$[;" | q[%'#;]) +. ("````'$ __" | "%$[``" | '%&!,') . ${$[;}; `$~$__>&$=`;

      Since there isn't any output, my guess is that perl is clever enough to look at this program and say, "why define a bunch of screwy-looking variables, assign a bunch of junk, and do nothing, if there is no output? Let's not play at all." But if there were output, would perl assign values to all the odd-looking $ variables like $= , $. , $~ , $___ ?

      What symbols would I look for if there were something pernicious happening?

        ... What symbols would I look for if there were something pernicious happening?

        You mean aside from the stuff I already mentioned?

        ppi_dumper can always help tell you which parts are which parts

        ...

        Danger :) I don't know that that obfu ever did anyting, but its fairly standard fare, try to hide when you're doing something tricy, transform nonsense into a message (or evil)

        #!/usr/bin/perl -- use strict; use warnings; use B::Deparse(); use Safe(); use Data::Dump qw/ dd /; use Perl::Tidy(); use PPI(); use PPI::Dumper(); #~ [id://630435] on Aug 03, 2007 at 03:44 UTC my $obfu3raw = <<'__RAW__'; $==$';$;||$.|$|;$_='*$(^@(%_+&~~;#~~/.~~;_);;.);;#);~~~~;_,.~~,.*+,./| +~~;_);@-,.;.);~~,./@@-__);;.);;#,.;.~~@-);;#);;;);~~,.*+,.;#);;;;#-(@ +-__);;.);;#,.;.~~@-););,./.);~~,./|,.*+,./|,.););;#;#-(@-__);;.);;#,. +;.~~@-;;,.,.*+,./@,.;.;#__;#__;;,.,.*+,./|,.;;;#-(@-__@-__,.;_);@-,.; +.,./|~~();.;#;.;;;;;;;;;.;.~~;.~~~~/@~~@-~~~~;#/|;#/|~~~~~~/@~~@-~~~~ +~~;_,.;;,.;.);,.~~;_,./|);;.,./@,./@~~~~~~*+;#-(@-__,.,.,.*+,./|,.;;~ +~()~~@-);;#);;.,.~~~~@-);-(@-__@-*+);~~,..%,.;;,.*+);~~~~@-,.*+,.,.~~ +@-~~.%,.;;~~@-,./.,./|,.;;~~@-~~.%););;#-(@-__@-*+);;.,./|,./@,.*+,./ +|,.-(~~@-,.*+,.,.~~@-~~.%,.,.~~@-,./.,./|,.;;~~@-~~.%););;#-(@-__);.% +~~/@~~@-~~~~~~;_,.(),.;_,..%,.;.~~;_~~;;;#/|~~~~~~*+;#-(@-__);@-);~~, +.*+,./|);;;~~@-~~~~;;__;;/.;.@-;;();./@,./|~~~~;#-(@-__&$#%^';$__='`' +&'&';$___="````"|"$[`$["|'`%",';$~=("$___$__-$[``$__"|"$___"|("$___$_ +_-$[.%")).("'`"|"'$["|"'#").'/.*?&([^&]*)&.*/$'.++$=.("/``"|"/$[`"|"/ +#'").(";`/[\\`\\`$__]//`;"|";$[/[\\$[\\`$__]//`;"|";#/[\\\$\\.$__]//' +").'@:=("@-","/.","~~",";#",";;",";.",",.",");","()","*+","__","-("," +/@",".%","/|",";_");@:{@:}=$%..$#:;'.('`'|"$["|'#')."/(..)(..)/".("`` +`"|"``$["|'#("').'(($:{$'.$=.'}<<'.(++$=+$=).')|($:{$'.$=.'}))/'.("`` +`;"|"``$[;"|"%'#;").("````'$__"|"%$[``"|"%&!,").${$[}; return "$~$__>&$="; ##`$~$__>&$=`; __RAW__ my $obfushell = <<'__RAW__'; perl -0ne 's/.*?&([^&]*)&.*/$1/sg;s/[\t\n ]//g;@:=("@-","/.","~~",";#" +,";;",";.",",.",");","()","*+","__","-(","/@",".%","/|",";_");@:{@:}= +$%..$#:;s/(..)(..)/chr(($:{$1}<<4)|($:{$2}))/egs;eval' - >&2 perl -0ne ' s/.*?&([^&]*)&.*/$1/sg; s/[\t\n ]//g; @:=("@-","/.","~~",";#",";;",";.",",.",");","()","*+","__","-(","/@"," +.%","/|",";_"); @:{@:}=$%..$#:; s/(..)(..)/chr(($:{$1}<<4)|($:{$2}))/egs; eval ' - >&2 __RAW__ my $obfushelled = <<'__RAW__'; $/ = "\000"; $\ = undef; s/.*?&([^&]*)&.*/$1/sg;s/[\t\n ]//g;@:=("@-","/.","~~",";#",";;",";.", +",.",");","()","*+","__","-(","/@",".%","/|",";_");@:{@:}=$%..$#:;s/( +..)(..)/chr(($:{$1}<<4)|($:{$2}))/egs; $_; ##eval __RAW__ Dobfu( $obfu3raw ); Dobfu( $obfushelled ); exit( 0 ); sub Dobfu { my $dobfu = shift; print "\n\n##src2deparse\n", src2deparse( \$dobfu ), "\n\n##src2ppidumper\n", src2ppidumper( \$dobfu ), "\n\n##src2perltidy\n", src2perltidy( \$dobfu ), "\n\n##src2ppistrings\n", join "\n\n", src2ppistrings( \$dob +fu ), "\n\n##SafestUndumper src2ppistrings\n", SafestUndumper( "jo +in ',', ".join(',', src2ppistrings( \$dobfu ) ) ), "\n\n##SafestUndumper raw\n", join "\n\n", SafestUndumper( \ +$dobfu ), "\n\n"; } sub src2deparse { use Path::Tiny qw/ path /; use B::Deparse; my $modfile = shift or die "need one modfile or \'string'"; my $mod = ref $modfile ? $$modfile : path( $modfile )->slurp_raw; die "NO WAY STUPID!! " if $mod =~ /\b(?:BEGIN|CHECK|INIT|UNITCHECK +|END|sub)\b/; my $modsub = do { no strict; no warnings; eval " sub { return ; $ +mod } " or die $@ }; my $deparsed = B::Deparse->new( "-p", ## extra parens for disambiguation "-sC", ## cuddle elsif "-si2", ## indent by 2 instead of 4 )->coderef2text( $modsub ) ; return $deparsed; } sub src2ppidumper { use PPI(); use PPI::Dumper(); my $modfile = shift or die "need one modfile or \'string'"; my $Module = PPI::Document->new( $modfile, readonly => 1, ); my $errstr = PPI::Document->errstr ; my $Dumper = PPI::Dumper->new( $Module, memaddr => 0, indent => 2, class => ! 0, content => ! 0, whitespace => ! 1, comments => ! 0, locations => 0, ); $Dumper = $Dumper->string; ## grr wantarray ? return( $Dumper, $errstr ) : return( $Dumper );; } sub src2perltidy { use Perl::Tidy(); my $modfile = shift or die "need one modfile or \'string'"; my $html = ""; my $stderr = ""; Perl::Tidy::perltidy( source => $modfile, destination => \$html, stderr => \$stderr, ); wantarray ? return( $html, $stderr ) : return( $html ); } use PPIx::XPath; use Tree::XPathEngine; sub PPI::Token::xf { goto &PPI::Node::xf } sub PPI::Node::xf { my( $node, $query ) = @_; $query = PPIx::XPath->clean_xpath_expr( $query ); $::pxp ||= Tree::XPathEngine->new(); return $::pxp->findnodes( $query, $node ); } sub src2ppistrings { use PPI(); use PPI::Dumper(); my $modfile = shift or die "need one modfile or \'string'"; my $Module = PPI::Document->new( $modfile, readonly => 1, ); my $errstr = PPI::Document->errstr ; die "Could not parse [$modfile] for PPI: $errstr " if $errstr; return map { "$_" } $Module->xf( q{ //Token::Quote::Double | //Token::Quote::Single } ); ## | //Token::Quote::Interpolate ## the potential evil ones ## qq{ ## evil @{[ eval die ]} ## evil @{[ `exit` ]} ## } } sub SafestUndumper { use Safe; my $s = Safe->new; # http://perl5.git.perl.org/perl.git/blob?f=regen/opcodes $s->permit_only( "anonlist", "anonhash", "pushmark", # perlcall says "PUSHMARK macro tells Perl to make a mental note of th +e current stack pointer." "const", "undef", "list", "lineseq", "padany", "leaveeval", # needed for Safe to operate, is safe without +entereval # needed for self-referentialnes "sassign", "rv2sv", "rv2hv", "helem", # dd-style do-block self-referentialnes "enter", "null", ); local $@; my(@ret)=$s->reval( @_ ); warn $@ if $@; return @ret; } ## end sub SafestUndumper __END__

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1133507]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (6)
As of 2024-04-24 07:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found