Tuesday, March 27, 2007

As Borat would Say: Ruby Makes Me More Productive ...Pause... NOT!

So there are are lots of claims/hype/nonsense about how Ruby makes more productive. I guess those folks have never used Python: which "just work" and has a not only a richer built-in API but one that is much better documented.

A case in point:

So I've just spent the last hour (maybe up to two now..) or so trying to get basic OS information, you know that is built in the standard python library platform so you can do stupide Hello World style stuff like:

root@ubuntu:~/rubygems-0.9.2# python
Python 2.4.3 (#2, Oct 6 2006, 07:52:30)
[GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.uname()
('Linux', 'ubuntu', '2.6.15-23-server', '#1 SMP Tue May 23 15:10:35 UTC 2006', 'i686', '')

Which of course isn't built into the standard library and if it were you wouldn't be able to find out if it were anyway.

All bitterness behind me, I was pretty excited when I found sysutils on RubyForge. That looks niiiiice.

First I try OSX... where 50% of the gem's fail to install (but to the projects credit there are some tracker items opened this month), but sys-uname appears to sort of install, but of course it doesn't work when I try to use it:

franz-g4:~ mdfranz$ gem list | grep sys
sys-uname (0.7.4)
sys-uptime (0.4.5)
A Ruby interface for getting system uptime information.
franz-g4:~ mdfranz$ irb
irb(main):001:0> require 'sys/uname'
LoadError: no such file to load -- sys/uname
from (irb):1:in `require'
from (irb):1

What could it be? Dueling Ruby's (I use ruby from ports in /opt in addition to the built in "broken" Ruby in Tiger) Maybe it is an OSX (or PPC) issue because of course nobody uses Ruby on OSX, well except all those fancy rails developers who have no use for APIs like these.

So I switch over to OpenBSD 4.1. Download gems 0.9.2. And try to install. More than half fail with even worse errors --configure errors. I try the source for sys-uname and it sort of installs. Ri/Rdoc don't work (path problems?!) but I manage to find the API documentation deep in /usr/local/lib/ after installing links from ports, since lynx doesn't support frames.

But as you can see installing from source is a bad idea, because apparently that only works for Win32, which makes complete sense since BSD folks only install binaries and Windows users have this obscure method involving something called "make"

# irb
irb(main):001:0> require 'sys/uname'
LoadError: no such file to load -- win32ole
from /usr/local/lib/ruby/site_ruby/1.8/sys/uname.rb:1:in `require'
from /usr/local/lib/ruby/site_ruby/1.8/sys/uname.rb:1
from (irb):1:in `require'
from (irb):1
irb(main):002:0> # vi /usr/local/lib/ruby/site_ruby/1.8/sys/uname.rb
# irb
irb(main):001:0> require 'sys/uname'
=> true
irb(main):002:0> import Sys
NoMethodError: undefined method `import' for main:Object
from (irb):2
irb(main):003:0> Uname
NameError: uninitialized constant Uname
from (irb):3
irb(main):004:0> include Sys
=> Object
irb(main):005:0> Uname.sysname
NameError: uninitialized constant Sys::Uname::WIN32OLERuntimeError
from /usr/local/lib/ruby/site_ruby/1.8/sys/uname.rb:101:in `sysname'
from (irb):5
from :0
irb(main):006:0> Sys
=> Sys
irb(main):007:0> Sys.methods
=> ["methods", "instance_eval", "display", "object_id", "dup", "instance_variables", "include?", "private_instance_methods", "instance_of?", "protected_method_defined?", "extend", "const_defined?", "eql?", "name", "public_class_method", "hash", "id", "singleton_methods", "autoload", "taint", "constants", "frozen?", "instance_variable_get", "kind_of?", "ancestors", "to_a", "private_class_method", "const_missing", "instance_method", "type", "instance_methods", "protected_methods", "method_defined?", "instance_variable_set", "const_get", "is_a?", "respond_to?", "to_s", "module_eval", "class_variables", "class", "<=>", "<", "tainted?", "private_methods", "==", "public_instance_methods", "autoload?", "__id__", "===", "public_method_defined?", ">", "included_modules", "nil?", "untaint", "const_set", "method", ">=", "<=", "send", "inspect", "class_eval", "clone", "=~", "protected_instance_methods", "public_methods", "private_method_defined?", "__send__", "equal?", "freeze"] irb(main):008:0> Uname
=> Sys::Uname
irb(main):009:0> Uname.methods
=> ["methods", "instance_eval", "display", "object_id", "dup", "instance_variables", "include?", "private_instance_methods", "instance_of?", "protected_method_defined?", "extend", "const_defined?", "eql?", "nodename", "name", "public_class_method", "hash", "new", "id", "singleton_methods", "release", "autoload", "taint", "constants", "frozen?", "instance_variable_get", "parse_ms_date", "kind_of?", "ancestors", "to_a", "private_class_method", "const_missing", "instance_method", "type", "protected_methods", "instance_methods", "uname", "superclass", "convert", "method_defined?", "instance_variable_set", "const_get", "is_a?", "respond_to?", "to_s", "machine", "module_eval", "class_variables", "allocate", "class", "<=>", "<", "tainted?", "private_methods", "==", "public_instance_methods", "autoload?", "__id__", "===", "public_method_defined?", ">", "sysname", "included_modules", "nil?", "untaint", "const_set", "method", ">=", "<=", "send", "inspect", "class_eval", "version", "clone", "=~", "protected_instance_methods", "public_methods", "private_method_defined?", "__send__", "equal?", "freeze"] irb(main):010:0> Uname.machine
NameError: uninitialized constant Sys::Uname::WIN32OLERuntimeError
from /usr/local/lib/ruby/site_ruby/1.8/sys/uname.rb:140:in `machine'
from (irb):10
from :0

So this obviously isn't working. Delete the package I installed from source which is obviously only meant to be used on Windows. So I think to myself, maybe shouldn't be wasting my time with Ruby on non-standard OS's like OpenBSD and OSX that nobody in their right mind uses in corporate environments. Things will fair much better on Linux:

root@ubuntu:~# apt-get install ruby-1.8.4
Reading package lists... Done
Building dependency tree... Done
E: Couldn't find package ruby-1.8.4
root@ubuntu:~# apt-get install ruby
Reading package lists... Done
Building dependency tree... Done
The following extra packages will be installed:
libruby1.8 ruby1.8
Suggested packages:
ruby1.8-examples rdoc1.8 ri1.8
The following NEW packages will be installed:
libruby1.8 ruby ruby1.8
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 1627kB of archives.
After unpacking 6058kB of additional disk space will be used.
Do you want to continue [Y/n]? y
Get:1 http://security.ubuntu.com dapper-security/main libruby1.8 1.8.4-1ubuntu1.3 [1420kB]
Get:2 http://us.archive.ubuntu.com dapper/main ruby 1.8.2-1 [19.0kB]
Get:3 http://security.ubuntu.com dapper-security/main ruby1.8 1.8.4-1ubuntu1.3 [189kB]
Fetched 1627kB in 3s (408kB/s)
Selecting previously deselected package libruby1.8.
(Reading database ... 60327 files and directories currently installed.)
Unpacking libruby1.8 (from .../libruby1.8_1.8.4-1ubuntu1.3_i386.deb) ...
Selecting previously deselected package ruby1.8.
Unpacking ruby1.8 (from .../ruby1.8_1.8.4-1ubuntu1.3_i386.deb) ...
Selecting previously deselected package ruby.
Unpacking ruby (from .../archives/ruby_1.8.2-1_all.deb) ...
Setting up libruby1.8 (1.8.4-1ubuntu1.3) ...

Setting up ruby1.8 (1.8.4-1ubuntu1.3) ...
Setting up ruby (1.8.2-1) ...
root@ubuntu:~# ruby -v
ruby 1.8.4 (2005-12-24) [i486-linux]
root@ubuntu:~# wget http://rubyforge.org/frs/download.php/17190/rubygems-0.9.2.tgz
--21:07:50-- http://rubyforge.org/frs/download.php/17190/rubygems-0.9.2.tgz
=> `rubygems-0.9.2.tgz'
Resolving rubyforge.org...
Connecting to rubyforge.org||:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://rubyforge.iasi.roedu.net/files/rubygems/rubygems-0.9.2.tgz [following]
--21:07:51-- http://rubyforge.iasi.roedu.net/files/rubygems/rubygems-0.9.2.tgz
=> `rubygems-0.9.2.tgz'
Resolving rubyforge.iasi.roedu.net...
Connecting to rubyforge.iasi.roedu.net||:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 197,256 (193K) [application/x-gzip]

100%[====================================>] 197,256 203.00K/s

21:07:52 (202.37 KB/s) - `rubygems-0.9.2.tgz' saved [197256/197256]

root@ubuntu:~# tar xzvf rubygems-0.9.2.tgz


root@ubuntu:~# cd rubygems-0.9.2
root@ubuntu:~/rubygems-0.9.2# ls
bin examples lib post-install.rb redist setup.rb
ChangeLog gemspecs LICENSE.txt Rakefile Releases test
doc GPL.txt pkgs README scripts TODO
root@ubuntu:~/rubygems-0.9.2# vi README
root@ubuntu:~/rubygems-0.9.2# ruby setup.rb
---> bin


<--- bin ---> lib
source_info_cache.rb /usr/local/lib/site_ruby/1.8/rubygems
install source_info_cache_entry.rb /usr/local/lib/site_ruby/1.8/rubygems
install specification.rb /usr/local/lib/site_ruby/1.8/rubygems
install timer.rb /usr/local/lib/site_ruby/1.8/rubygems
install user_interaction.rb /usr/local/lib/site_ruby/1.8/rubygems
install validator.rb /usr/local/lib/site_ruby/1.8/rubygems
install version.rb /usr/local/lib/site_ruby/1.8/rubygems
<--- lib/rubygems <--- lib Successfully built RubyGem Name: sources Version: 0.0.1 File: sources-0.0.1.gem Removing old RubyGems RDoc and ri... /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- rdoc/rdoc (LoadError) from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require' from /root/rubygems-0.9.2/./post-install.rb:103:in `install_rdoc' from /root/rubygems-0.9.2/./post-install.rb:118:in `try_run_hook' from setup.rb:584:in `run_hook' from setup.rb:1322:in `exec_task_traverse' from setup.rb:1175:in `exec_install' from setup.rb:894:in `exec_install' from setup.rb:712:in `invoke' from setup.rb:681:in `invoke' from setup.rb:1359

In hindsight, this is because I haven't installed the rdoc package, but I'm so frustrated I go back to OpenBSD. So I delete the what I've installed from source and try again with the GEM. Just for grins I go into the test script:

# cd /tmp
# ls
sys-uname-0.7.4 sys-uname-0.7.4.tar.gz
# cd sys-uname-0.7.4 # ls
MANIFEST doc install.rb sys-uname.gemspec
Makefile examples lib test
# cd test/ # ls
# ./tc_uname.rb ksh: ./tc_uname.rb: cannot execute - Permission denied
# ruby tc_uname.rb Loaded suite tc_uname
"architecture" test skipped on this platform
."srpc_domain" test skipped on this platform
."hw_provider" test skipped on this platform
."hw_serial_number" test skipped on this platform
."id" test skipped on this platform
."isa_list" test skipped on this platform
.."model" test skipped on this platform
.."platform" test skipped on this platform
.."srpc_domain" test skipped on this platform
Finished in 0.018236 seconds.

16 tests, 22 assertions, 0 failures, 0 errors

Ok so maybe something is working.

# irb
irb(main):001:0> require 'sys/uname'
=> true
irb(main):002:0> import Sys
NoMethodError: undefined method `import' for main:Object
from (irb):2
irb(main):003:0> include Sys
=> Object
irb(main):004:0> Uname
=> Sys::Uname
irb(main):005:0> Uname.methods
=> ["methods", "instance_eval", "display", "sysname", "object_id", "dup", "instance_variables", "include?", "private_instance_methods", "instance_of?", "protected_method_defined?", "extend", "const_defined?", "eql?", "name", "public_class_method", "hash", "new", "id", "singleton_methods", "nodename", "autoload", "taint", "constants", "frozen?", "instance_variable_get", "kind_of?", "ancestors", "to_a", "private_class_method", "const_missing", "instance_method", "type", "protected_methods", "instance_methods", "superclass", "machine", "method_defined?", "instance_variable_set", "const_get", "is_a?", "respond_to?", "to_s", "module_eval", "class_variables", "allocate", "class", "<=>", "<", "tainted?", "private_methods", "==", "public_instance_methods", "autoload?", "__id__", "===", "public_method_defined?", ">", "release", "included_modules", "nil?", "untaint", "const_set", "method", ">=", "<=", "send", "inspect", "class_eval", "version", "clone", "=~", "protected_instance_methods", "public_methods", "private_method_defined?", "uname", "__send__", "equal?", "freeze"] irb(main):006:0> Uname.sysname
=> "OpenBSD"
irb(main):007:0> Uname.display
Sys::Uname=> nil
irb(main):008:0> Uname.name
=> "Sys::Uname"
irb(main):009:0> Uname.machine
=> "i386"
irb(main):010:0> Uname.uname

=> #struct sysname="OpenBSD" nodename="pre41.my.domain" machine="i386"version="GENERIC#0" release="4.1"


Update: things are going much better after reading Rubygems got you down.

# gem install sys-uname
ERROR: While executing gem ... (NoMethodError)
undefined method `refresh' for #
# find / -name "source_cache" -exec rm {} \;
# gem update
Updating installed gems...
^C^C^C^C^[[CBulk updating Gem source index for: http://gems.rubyforge.org
Gems: [] updated
# gem install sys-uname
Bulk updating Gem source index for: http://gems.rubyforge.org
Select which gem to install for your platform (i386-openbsd4.0)
1. sys-uname 0.7.4 (ruby)
2. sys-uname 0.7.4 (mswin32)
3. sys-uname 0.7.3 (ruby)
4. sys-uname 0.7.2 (ruby)
5. Skip this gem
6. Cancel installation
> 1
Building native extensions. This could take a while...
Successfully installed sys-uname-0.7.4
Installing ri documentation for sys-uname-0.7.4...
Installing RDoc documentation for sys-uname-0.7.4...
# irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'sys/uname'
=> true
irb(main):003:0> include Sys
=> Object
irb(main):004:0> Uname
=> Sys::Uname
irb(main):005:0> Uname.methods
=> ["to_a", "to_yaml_style", "respond_to?", "ancestors", "module_eval", "const_missing", "type", "yaml_as", "protected_methods", "instance_methods", "public_method_defined?", "superclass", "eql?", "version", "instance_variable_set", "const_get", "is_a?", "autoload?", "machine", "hash", "to_s", "to_yaml_properties", "send", "display", "class_eval", "class_variables", "class", "method", "private_methods", "tainted?", "public_instance_methods", "__send__", "private_method_defined?", "untaint", "included_modules", "const_set", "id", "release", "instance_eval", "inspect", "require_gem", "taguri", "clone", "public_methods", "yaml_tag_class_name", "taguri=", "to_yaml", "protected_instance_methods", "extend", "protected_method_defined?", "gem", "freeze", "require", "yaml_tag_read_class", "public_class_method", "allocate", "__id__", "<=>", "<", "uname", "methods", "==", "===", ">", "dup", "nil?", "instance_variables", "include?", "private_instance_methods", ">=", "instance_of?", "const_defined?", "sysname", "<=", "name", "private_class_method", "autoload", "=~", "object_id", "new", "singleton_methods", "method_defined?", "taint", "equal?", "instance_method", "instance_variable_get", "frozen?", "constants", "kind_of?", "nodename", "yaml_tag_subclasses?"]
irb(main):009:0> Uname.uname
=> #sysname="OpenBSD" nodename="xxxxx.hewitt.com" machine="i386" version="GENERIC#1107" release="4.0">

Saturday, March 24, 2007

Fun with Introspection on Public XML-RPC Servers

Somewhere I managed to find a list that someone had compile of sites that support XML-RPC (this is mostly for blog pingers) so for grins I tried iterating through them with system.listmethods() as I did for Python and Ruby previously. I guess I shouldn't have been surprised that so many had introspection enabled. The results?

A sampling of the server names that were returned:
  • Apache/1.3.33 (Debian GNU/Linux) PHP/4.3.10-18
  • Apache/1.3.29 (Unix) PHP/4.3.7
  • Apache/1.3.34 (Unix) mod_fastcgi/2.4.2 mod_ssl/2.8.25 OpenSSL/0.9.7e PHP/4.4.4 FrontPage/
  • Apache XML-RPC 1.0
  • Apache/2.0.55 (Ubuntu) PHP/5.1.2
  • psfe
  • Apache/2.0.52 (CentOS), X-Powered-By: PHP/5.1.6
  • Apache, X-Powered-By: PHP/4.4.2
  • SOAP::Lite/Perl/0.60
  • Apache Coyote 1.1

A lot of Apache servers (that didn't ID the XML-RPC implementation) returned message and flerror as valid methods. And the "expected result" for many others returned as little as:
  • system.listMethods
  • system.methodSignature
  • system.methodHelp
  • system.multicall
  • weblogUpdates.ping
  • weblogUpdates.extendedPing

While the juiciest spit out:
  • syndic8.GetFeedCount
  • syndic8.GetLastFeed
  • syndic8.FindFeeds
  • syndic8.QueryFeeds
  • syndic8.FindSites
  • syndic8.GetFeedInfo
  • syndic8.FindUsers
  • syndic8.GetUserInfo
  • syndic8.SuggestDataURL
  • syndic8.SuggestSiteURL
  • syndic8.GetLicenses
  • syndic8.CreateSubscriptionListFromOPML
  • syndic8.CreateSubscriptionListFromHTML
  • weblogUpdates.Ping
  • weblogUpdates.ping
And a whole lot more...

Some the servers that did not return any method names happily returned that system object wasn't present:

java.lang.Exception: RPC handler object "system" not found and no default handler registered

Can't evaluate the
expression because the name "system.listMethods" hasn't been defined.

Failed to access class (system): Perl v65280.0.0 required
(did you mean v65280.000?)--this is only v5.8.6,
stopped at (eval 119) line 1.\n

Is this a shock? No. Are these information disclosures the the end of the world? Certainly not. There are most likely all public API meant to be exposed to the world. What concerns me is the relatively small number of XML-RPC vulnerabilities that have been disclosed so far (CVE 2005-0089, CVE 2005-1921, CVE 2005-2498, CVE-2005-1992). I probably missed a few. Python, Ruby, and PHP PEAR XML-RPC implementations have all had shell command execution and object/method permission access issues. I guess only time will tell about the quality their Java and Perl counterparts and any other implementations out there.

Friday, March 23, 2007

Pebble Rocks!

Yes, Spring Mania (and no, I don't mean "March Madness" although it certainly fits!) has kept me up again. Either that or the first decent Italian Beef I had at Portillo's this evening.

So the only more fun than building Virtual Machines, is installing .war's. One of the areas where J2EE (or a subset thereof, in my case meaning Tomcat) has got Python and Ruby beat is the ease of deploying apps. No GEM interrogation (selection 1, 2, or 3) dependencies or Cheese Shop silliness (don't even get me talking about the Turbogears Python installer, almost as bad as compiling uClinux (which also rocks, BTW). Untar, find the .war, browse, deploy go to the URL.

On the wiki front Snipsnap and JSPWiki are pretty good that way, but I'm impressed with Pebble. It literally only took 10 minutes to get a first blog, and that was after pulling down a JDK, Tomcat 5.5.23 (which apparently fixed a bug with the manager servlet bug I ran into several weeks back when I was trying to the JSON-RPC server demo to work?!) set JAVA_HOME, change tomcat-users.xml.

And what's amazing it is quick and even with all this cruft:

root@ubuntu:~/apache-tomcat-5.5.23/webapps/pebble/WEB-INF/lib# ls
acegi-security-1.0.1.jar jstl-1.1.2.jar
asm-1.5.3.jar log4j.jar
asm-attrs-1.5.3.jar lucene-1.4.1.jar
asm-util-1.5.3.jar oro-2.0.8.jar
cglib-2.1_3.jar pebble-2.0.1.jar
commons-beanutils.jar radeox-1.0-beta2.jar
commons-codec-1.3.jar saxpath.jar
commons-collections-3.1.jar spring-beans-1.2.8.jar
commons-fileupload-1.0.jar spring-context-1.2.8.jar
commons-httpclient-3.0-rc3.jar spring-core-1.2.8.jar
commons-lang-2.1.jar spring-dao-1.2.8.jar
commons-logging-1.0.4.jar spring-web-1.2.8.jar
delicious-1.10.jar standard-1.1.2.jar
ehcache-1.2.1RC.jar tidy.jar
jcaptcha-all-1.0-RC3.jar xmlrpc-1.2-b1.jar

Of course this is within a VM with 384MB on a 2.8ghz/2G Pentium-D, but am I tempted to try this on a 96M Xen slice?

Thanks, but no thanks I'll stick with blogger.

Thursday, March 22, 2007

GrApple: Best OSX Theme for Firefox

I stumbled across the GrApple Theme for Firefox a couple of weeks ago and installed it on my MacBook and now can't live with out it. It is much better than the other Carbon lookalike themes such as iFox Graphite. Simple clean and reminds of the the amazing 640x400 monochrome display on my old Atari ST back in '87.

Wednesday, March 21, 2007

Yesterday's Firefox Update, AttackAPI, and Jikto (or the need for a CVSBSI)

So the auto-update for Firefox on my Powerbook hit last light (amazing how much I still find myself using my old 12" G4 despite the new box) and I briefly looked at the release notes. I was busy trying to get a release compiled for OpenBSD 4.1 in under a couple of hours while running under VMWare, so I saw port scanning, browser, Javascript, FTP PASV, blah blah blah andwent back to man release. But given all the hype about the upcoming Jikto release, I remembered PDP's attackapi and was looking for examples when I ran across the white paper and a nice blog providing summary of the technique and the vulnerability behind the new Firefox release. So I read it.

Although the "Manipulating FTP Clients Using The PASV Command" article is well written and organized (I hate LaTeX generated docs, though) I was overwhelmingly left with a feeling of so what, everybody had to do a Firefox update for this? I think there needs to a Common Vulnerability Scoring Bullshit Index (aka CVSBI, hmm this sounds like right up the alley for CIAG Research, wonder if Andrew can free up some cycles from AGA-12 for this, or maybe this doesn't rise to the level of "CI?") that is perfectly valid but ranks the number of "hoops" the attacker (or victim) has to jump through to get it to work. Maybe it is just me, but something felt cheap about an attack that require victims to be lured vis a XSS to a rogue FTP server (there a multiplier of at least 2 here?!) that users the user name and password as the C2 channel for scanner. And all just to be able "sort of" scan ports (meaning being able to distinguish between closed/wrapped and open/filtered) and maybe to get banners and (oh my God, the end is near!) fingerprint services based on the time that they respond. And there were subtle other limitations/pre-conditions in the attack.

At some point (not sure if this vuln is at that point) you get to the "solar system aligning" type vulnerability which is quite trivial to demonstrate under lab conditions but is more difficult in the real world (A case in point, I put TCP Reset Attacks against BGP into this category).

And that is why we need the CVSBI. Yes, the Firefox FTP client implementation could have been tighter and, yes, this should have been fixed eventually, but it seems this could have been rolled into the next update where we could have rolled up a number of these vulns that score moderate to high on the CSVBI.

Tuesday, March 20, 2007

Dark Reading on Two Topics that were Interesting in 1998

I had only been at Trident a few months when the ASIM developers (where were on the same floor in the building overlooking San Pedro and 410) were pondering Insertion, Evasion, and Denial and gloating about how it impacted NetRanger. At this time there was a lot of jealousy at Trident following the Wheel Group acquisition. And then by that summer, the geniuses at Trident management (yeah let's get retired Air Force O-5's to run a company, good one) cooked up the idea to spinoff their scanning tool (Network Toolbox was it called).

Somewhere around then I ran across the Apostols (yes, go to their website now and see what it says compared to back in the day) tool, Queso and I added it to the network security class I taught. Good stuff. We ran it and all the other tools in the class against the Trident corporate site and it only took about a day and a half (or maybe it was 5 hours) for the director of IT for the San Antonio region to come running up the stairs breathlessly with RealSecure printouts. SYN scans, my ass.

Happy times. Lots of drinking margaritas during lunch and working on Trinux waiting for the Veridian deal to close so I could cash out my stock options and be able to afford a 140k 3-1 in Crestview. We'll leave the bit about CCTI for another blog. And lots left about working at the phone company. Talk about dull.

In 2000 (a few months after starting at Cisco) I wasted several man-months on a project that compared the TCP sequence number plots of dozens of trains of IOS/CatOS on dozens of different hardware platforms. I even did some bullshit statistics in Excel that showed how it was sort of possible to determine which version of IOS was running on a box, well if you had all the data I had.

Sort of interesting, especially if you've got all the time on your hands and a lab full of Cisco gear. But an Utter waste of time. A truly "academic" endeavor, disrespect intended. Grab fingerprint then attack! is something along these lines.

Saturday, March 17, 2007

The Quest for JSON-RPC [In]Security

In Web 2.0-land, things really seem to be heating up with the not so interesting (or, in and of itself no more interesting than XML) JSON and the more interesting (as a lightweight replacement for XML-RPC or XMLHttpRequest used in AJAX apps) JSON-RPC to the point hat I've started tracking things on a wiki page.

To break down, here is what is sent on the wire. JSON-RPC is quite similar to XML-RPC except instead of sending and XML message via an HTTP POST you use JSON, such as:

POST http://jsolait.net/services/test.jsonrpc HTTP/1.1
Host: jsolait.net
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv: Gecko/20070219 Firefox/
Accept: text/xml, application/xml, application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: text/plain
Referer: http://jsolait.net/wiki/examples/jsonrpc/tester
Content-Length: 99
Pragma: no-cache
Cache-Control: no-cache

{"id":"httpReq", "method":"args2Array", "params":["hello", {"a": 1234, "b": "five"}, [6, 7, 8, 9]]}HTTP/1.0 200 OK

And obviously the server will respond with something like

HTTP/1.0 200 OK
Date: Wed, 28 Feb 2007 22:32:17 GMT
Server: Apache/2.0.55 (Debian) DAV/2 SVN/1.3.2 mod_python/3.2.10 Python/2.4.4
Content-Type: text/plain; charset=UTF-8
Proxy-Connection: close

{"id":"httpReq", "result":["hello", {"a":1234, "b":"five"}, [6, 7, 8, 9]], "error":null}

In terms of security, so far I've only found one presentation at CCC somewhat related to JSON-RPC security and I didn't find it terribly interesting. This article by Metaeye Security Group was a bit better, but had nothing to do with RPC, only JSON. Then there is the August 2006 Working Draft we see the following as a "non-goal:"

JSON-RPC does not address security, correlation, reliability, sessions management, state management and many other features that may be found in other RPC protocols or distributed systems. Developers are instead encouraged to employ features of HTTP and related Internet technologies that can offer some of these features in an equally compelling, robust and satisfying manner. Some features may be defined as extensions by other specifications.

Now it is tempting to mock this sentiment as the normal "security never makes it into 1.0 argument" (or think TCP/IP protocols that say, hey IPSEC will handle all security -- or our web application is secure if it goes over HTTPS) but there is actually some sense to their logic.

In an ideal world, it would definitely be better to rely on the web [application] server, web framework (or whatever) to do session management, authorization, authentication, and definitely encryption. Why reinvent the wheel, badly? However in practice this normally means form based authentication plus a strong session ID (whether Java, PHP, ASP, or whatever) all over SSL. But what if I don't want that or can't have that, for example if I'm not running the client within a browser--and yes I could definitely see some use cases for non-browser based JSON-RPC (as an alternative to SOAP or XML-RPC) and given the number of implementations out there some other folks obviously do as well.

Since HTTP Digest Authentication is not a pervasive or a robust as we would like (a case in point: try to find a Python/Ruby/Java based Web Server to expose XML-RPC web services that supports RFC 2617--actually Ruby seems the best choice) you are stuck with Basic Authentication over HTTPS. What I wish is that there were a lightweight authentication and authorization mechanism comparable to what Amazon does with S3 and its REST APIs.

If you are doing "RPC stuff" over HTTP, you need more granular security mechanisms than can be provided by HTTP[S] or the appserver.

Right now the big boys in web service land (Amazon, Google, Yahoo) don't seem to be exposing their APIs with JSON-RPC yet (although they do expose data in JSON) similar to the way that SOAP and REST are currently used, but it is probably only a matter of time. The other aspect of JSON-RPC that is sort of interesting is that several of the JSON-APIs are borrowing heavily from their XML-RPC counterparts, which have had their share of security issues--at least in PHP, Python, and Ruby.

I just ran across some fresh blogs (last week) on JSON (In)Security that are relevant:
Securing your JSON and JSON is not as safe as people think it is.

I will continue to add stuff like this to my Json Wiki Page as I find it.

Fun & Games on openbsd-misc re: ICMPv6 Vuln

After getting burned out blogging on vulnerability disclosure I didn't think I'd bring the topic up here, but I couldn't resist. As of this morning, there have been about 75 emails "discussing" to the ICMPv6 mbuf vuln discovered by Core Security.

Although stuff like this is rather amusing.
You know, Theo, it makes me fucking sick to see you treat the community of
people who support your project and pay your wage like this. It makes me
even sicker to see the crowds of shrill, stupid fanboys on this list who are
so pathetically eager to agree with you that that they support even your
most unreasonable, childish and frankly stupid statements. You are a goddam
hypocrite - either you do OpenBSD purely for yourself and the other
developers (in which case I will stop financially supporting the project,
and everyone else should too) or you recognise that what really keeps
OpenBSD going is the group of people that advocate OpenBSD, use it in the
real world, and buy your goddamn CDs and t-shirts to keep you going..

My only comment to that is the OpenBSD community need more Ubuntu spirit!

Theo's recap/clarifications on interactions with Core is definitely worth reading:

Noone in OpenBSD is pissed off about this. We posted the bug fix as
soon as we became aware of the problem. The timeline goes like this:

1) We were told there was a mbuf crash, which could remotely CRASH
the machine. There was no proof that more could be done, not even
a whiff.

2) We commited the fix, about 24 hours later. It took a few days to
get the errata up because the people who do that were at a conference.
It was labelled as a RELIABILITY FIX because everyone felt it was just
a CRASH. I then entered into a long conversation with Core explaining
why we label crash fixes (even remote) as RELIABILITY FIXES.

3) Core felt maybe something more could be done and continued working,
and ONE WEEK LATER later, finally managed to show us brand new code
which showed that intrusion was possible. Before that moment, it
was still just confirmed to be a CRASH.

4) A few hours after we become aware that it was more than a CRASH, we
changed the advisory to say it was a real security risk. We first had
to get the patch into -stable,

Thursday, March 15, 2007

Comparing Tempfile API's in Python and Ruby

Much to the detriment of my productivity at work, I've been bouncing back and forth between Python and Ruby. But it makes for some interesting API's comparisons. So I'll continue the motif I started with looking at Ruby and XML-RPC API's by comparing temp files APIs.

First the security background, Insecure temp file usage is a notorious (but typically local) security flaw that can lead arbitrary code execution, , escalation of privilege, and denial of service--commonly as a result of race conditions. Take a search Packetstorm and you'll see a bunch of examples of what not to do.

So let's take a look at the tempfile module in the Ruby Standard library:

new(basename, tmpdir=Dir::tmpdir)

Creates a temporary file of mode 0600 in the temporary directory whose name is basename.pid.n and opens with mode "w+". A Tempfile object works just like a File object.

If tmpdir is omitted, the temporary directory is determined by Dir::tmpdir provided by ‘tmpdir.rb’. When $SAFE > 0 and the given tmpdir is tainted, it uses /tmp. (Note that ENV values are tainted by default)

How would you use this?
franz-g4:~ mdfranz$ irb
irb(main):001:0> require 'tempfile'
=> true
irb(main):002:0> t = Tempfile.new("ruby")
=> #
irb(main):003:0> u = Tempfile.new("ruby")

At this point you have a regular old File object you can do what ever you want to with. So the permissions are limited to the process owner and the filename is based on the process id, which depending on the OS may be more or less predictable. But the good news is the file is deleted automatically after the script finishes execution.
franz-g4:/tmp mdfranz$ ls -al ruby*
-rw------- 1 mdfranz wheel 0 Mar 15 20:36 ruby655.0
-rw------- 1 mdfranz wheel 0 Mar 15 20:37 ruby655.1

With Python we get an (ostensibly) more secure methods as part of the tempfile module:

mkstemp([suffix[, prefix[, dir[, text]]]])
Creates a temporary file in the most secure manner possible. There are no race conditions in the file's creation, assuming that the platform properly implements the O_EXCL flag for os.open(). The file is readable and writable only by the creating user ID. If the platform uses permission bits to indicate whether a file is executable, the file is executable by no one. The file descriptor is not inherited by child processes.

Unlike TemporaryFile(), the user of mkstemp() is responsible for deleting the temporary file when done with it.

If suffix is specified, the file name will end with that suffix, otherwise there will be no suffix. mkstemp() does not put a dot between the file name and the suffix; if you need one, put it at the beginning of suffix.

If prefix is specified, the file name will begin with that prefix; otherwise, a default prefix is used.

If dir is specified, the file will be created in that directory; otherwise, a default directory is used.

If text is specified, it indicates whether to open the file in binary mode (the default) or text mode. On some platforms, this makes no difference.

mkstemp() returns a tuple containing an OS-level handle to an open file (as would be returned by os.open()) and the absolute pathname of that file, in that order. New in version 2.3.

and for directories

mkdtemp([suffix[, prefix[, dir]]])
Creates a temporary directory in the most secure manner possible. There are no race conditions in the directory's creation. The directory is readable, writable, and searchable only by the creating user ID.

The user of mkdtemp() is responsible for deleting the temporary directory and its contents when done with it.

The prefix, suffix, and dir arguments are the same as for mkstemp().

mkdtemp() returns the absolute pathname of the new directory. New in version 2.3.

An example combining the two:

>>> import tempfile
>>> td = tempfile.mkdtemp()
>>> td
>>> tf = tempfile.mkstemp(dir=td)
>>> tf
(3, '/tmp/tmpaJ-M4J/tmpb7pxE4')

And even after the script executes they are still around

franz-macbook:/tmp mdfranz$ ls -al tmpaJ-M4J/
total 0
drwx------ 3 mdfranz wheel 102 Mar 17 07:08 .
drwxrwxrwt 4 root wheel 136 Mar 17 07:06 ..
-rw------- 1 mdfranz wheel 0 Mar 17 07:08 tmpb7pxE4

So unlike the Ruby you have to call open on the pathname + filename you got back from mkstemp/mktemp vs getting a File/IO object back.

As usual, the Python API documentation is more complete (and probably more importantly) consistent and in a single place. Going back and forth between the The Pragmatic Programmer and the the Ruby Standard Library Documentation and the Ruby Class and Library reference is very annoying. While I certainly didn't audit the source for Python or Ruby to determine the difficulty of race conditions and to see whether or not they are "impossible" but the use of a more pseuedo-random value for the filename (and directory name) with Python certainly raises the bar higher for Python. And combining the directory and file functions in Python. The only real downside for the Python API is that you have to clean up temporary files and directories automatically, likely a consequence of the Python API not being "truly and consistenly OO" (a common critique).

Tuesday, March 13, 2007

Comparing various OpenBSD sysctl hw dumps (real and virtual)

I'm even less capable of anything insightful tonight than normal (daughter still sick, Vicks fumes making me nauseous--or perhaps it was finally watching Borat tonight--not sure which scene was more more amazing: the "motel fight," proposing to Pamela Anderson or, speaking in tongues, although the bear scaring the children from the ice cream truck was fun, too), but needed to get something technical on the top blog.

To clear my head, did a quick pre-OpenBSD 4.1 install just now (nothing more pleasantly mind-numbering than OS installs and yes, there was a branch this week) on VMWare Fusion which has been rock solid with XP, 2K, Ubuntu, and even OpenBSD. Yeah, who needs this fancy Parallels crap.... Coherence... Whatever...

Oh and while I'm ranting... I certainly think outing OpenBSD bugs is fun (ah the sweet naivety of my first year in STAT, posting from from Cisco account and I didn't even get a nasty note from Jim Duncan) I think Bejtlich is off the mark on his "preview" IPv6 stack issues, 2007 is not going to be 1997. Codenomicon has had their IPv6 suite for ages and all the big boys have sent their Euros east and have been silently fixing "obscure" IPv6 bugs for years now.

Since the hardware info provided by sysctl has been of interest lately (for inventory control purposes) I thought I'd make some comparisons:

OpenBSD 4.0 on an old P-133 (my firewall)

# sysctl -a hw
hw.model=Intel Pentium (P54C) ("GenuineIntel" 586-class)

OpenBSD 4.1 (GENERIC #1435) on MacBook under VMWare

# sysctl -a hw
hw.model=Intel(R) Core(TM)2 CPU T7200 @ 2.00GHz ("GenuineIntel" 686-class)
hw.vendor=VMware, Inc.
hw.product=VMware Virtual Platform
hw.serialno=VMware-56 4d 9f f4 34 21 d5 19-b7 47 9e d3 a1 a6 02 7c

OpenBSD 3.9 (VMWare)

# sysctl -a hw
hw.model=Intel(R) Pentium(R) D CPU 2.80GHz ("GenuineIntel" 686-class)

OpenBSD 4.0 (VMWare)

# sysctl -a hw
hw.model=Intel(R) Pentium(R) D CPU 2.80GHz ("GenuineIntel" 686-class)
hw.vendor=VMware, Inc.
hw.product=VMware Virtual Platform
hw.serialno=VMware-56 4d 26 c0 f0 df 4d ad-f3 5d a8 6c d0 4e 1f 57

So the trend is definitely toward more CPU information (and at least on one of my Optiplex's at work, the hw.serialno field actually matches one of those Dell stickers on the box) don't know if this is the case for other vendors as well.

Boy that was boring, now I can sleep.

Sunday, March 11, 2007

Daylight Savings

So after 7 years in Austin, 5 years each in San Antonio and College Station -- what has been happening weather-wise over the last three days in Skokie has been very strange indeed. This on top of everything else that is foreign about this part of Chicagoland: the inescapable Russian and Arabic, fresh pitas and hummus, the absence of the laid-back hippie-yuppie vibe we grew so accustomed to (and loved) in central Austin, the politicians that brand their name everywhere (middle initial included) and do not talk like politicians in the South and could never get elected down there, the number of forms of identification required to register your kids for school, the puzzled looks you get when you ask about "breakfast tacos" or migas. Yes, today, with temperatures reaching 50 for the first time since we arrived in January, folks came out of the houses for the first time. Up until now it has mostly been "Shabbat walkers" along the sidewalks, or walking shoulder to shoulder across the middle of the street, en route to the synagogues on Main and Dempster on Saturday afternoons.

Despite all of this that is not Texas, there is definitely something refreshingly real about not living in one of the hippest, most sought-after, nationally ranked as a "best places" to live places that prides it self on its creativity and music and film and uniqueness, where everyone tell you "I hear that is great" (or shares their experiences on Sixth Street) when you tell you where you are from. That is definitely not Skokie. But driving along McCormick today, listening to Lucinda's new album for the first time (and I will listen to it again on the commute up the Edens tomorrow), I couldn't help but feel a little homesick.

After leaving Aggieland in 1994, we got to Austin as fast as we could. For so many years we would make the drive up I-35 on weekends through Schertz-Cibolo, New Braunfels, and then San Marcos (past the tacky outlets), then Onion Creek and finally down the hill where the traffic pools before downtown and the I-35 bridge across Town Lake. We finally made it around the pre-millenium Halloween, moving into what would be the first of two "fixer-uppers" within a mile of each other in 78757 (both which we managed to "flip" quite nicely). We passed our last painful childless Thanksgiving sitting on swings in Pease Park. As usual, it was muggy, and as usual, and the paper sacks were out on the picnic tables along Shoal Creek. And on the Millenium Eve we made it downtown trying to Lyle Lovett in front of the capitol building before it got too crowded to the point of compression, only to return home for the global letdown that was Y2K.

But from that year on, we marked Spring in Austin by the third week of Feburary. It officially began when the old Elms (and new Hackberrys that grew too close the green house (as Nikolas would call it) on Brentwood bloomed. For after a week and a half in Russia we returned not only with our new son but to Spring in Texas.

Saturday, March 10, 2007

Django mysite.settings madness

For fun, I've started writing a small firewall (meaning OpenBSD PF) monitoring webapp using Django, which I actually will be blogging on it real soon now -- but first on something that seemed rather confusing about the framework, and that has tripped up others as well.

In recent (at least 0.95 and 0.95.1 but probably much earlier) you don't actually have to set the environment variable DJANGO_SETTINGS_MODULE if you running the webserver with python manage.py runserver or access a shell with python manage.py shell, but you still have to set this enivironment variable -- or call settings.configure() -- if you are access any of your Django objects from another app.

While this is clearly documented on the web site, it still gives newbies trouble, especially when running Django with mod_python under Apache. In fact a couple of months ago it stumped me for a while,.

However, what was most confusing is that everywhere (in most of the examples) the variable is always defined to mysite.settings but it is ambigous what the "mysite" should be. Is mysite my actuall site (or project) or is it always set to mysite and Django knows automatically. So settings the settings.py that gets imported by your scripts, but is mysite a path name?

If I used the value "mysite.settings", I get the following:

franz-g4:~/Documents/dev/playin/pfwatch mdfranz$ export DJANGO_SETINGS_MODULE=mysite.settings
franz-g4:~/Documents/dev/playin/pfwatch mdfranz$ python shellstuff.py
Traceback (most recent call last):
File "shellstuff.py", line 4, in ?
from monitor.models import Source,Event
File "/Users/mdfranz/Documents/dev/playin/pfwatch/../pfwatch/monitor/models.py", line 1, in ?
from django.db import models
File "/opt/local/lib/python2.4/site-packages/django/db/__init__.py", line 7, in ?
if not settings.DATABASE_ENGINE:
File "/opt/local/lib/python2.4/site-packages/django/conf/__init__.py", line 27, in __getattr__
File "/opt/local/lib/python2.4/site-packages/django/conf/__init__.py", line 52, in _import_settings
raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE
EnvironmentError: Environment variable DJANGO_SETTINGS_MODULE is undefined.

To back up a bit, I have the following files in my project directory:

franz-g4:~/Documents/dev/playin/pfwatch mdfranz$ ls
__init__.py manage.py pfwatch.sql settings.pyc urls.py
__init__.pyc monitor settings.py shellstuff.py urls.pyc
franz-g4:~/Documents/dev/playin/pfwatch mdfranz$

So monitor is an application directory where I have my models defined and shellstuff.py is a script that I want to be able to access my models, meaning my data, which fails if I use mysite.settings. It also fails if I use pfwatch.settings both from within the current directory or if I step up a level.

So how did I finally get it sort of working? The solution is just to set DJANGO_SETTINGS_MODULE to just settings.

Of course this blog entry will make a lot more sense once you read my next one.

Thursday, March 08, 2007

security.apple.com/fuzzers (or why Apple is Smarter than Microsoft and Cisco)

At Apple they obviously have some internal site on fuzzing instead of googling all day long

xxxxxxx.apple.com - - [09/Mar/2007:00:20:15 +0000] "GET /secwiki/FuzzingTools HTTP/1.1" 200 36039 "http://security.apple.com/fuzzers" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3" "-"

But at Microsoft and Cisco they are still doing stupid shit like this:

xxxxxxxx.cisco.com - - [22/Jan/2007:07:10:11 +0000] "GET /secwiki/ISIC HTTP/1.1" 200 11929 "http://www.google.com/search?q=Mike+Frantzen+isic&hl=zh-CN&amp;amp;amp;amp;amp;lr=&newwindow=1&start=10&sa=N" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)" "-"


xxxxxxx.microsoft.com - - [04/Jan/2007:04:47:16 +0000] "GET /secwiki/FuzzingTools HTTP/1.1" 200 45940 "http://www.google.com/search?hl=en&q=fuzzing+files" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)" "-"

Sigh. Just imagine the sheer excitement of running ISIC over and over again on infinite number of IOS hardware/software combinations. The Horror....

Wednesday, March 07, 2007

From GWT to pyGWT (aka pyjamas)

I'm not sure how I stumbled across pyjamas (but it might have something to do with taking the day off to look after my kids) and it is definitely unclear how I will every use this but--it is basically a Python version of Google Web Toolkit where by you define and implement your UI's in Python (or Java in the case of GWT) which in turn gets translated into JavaScript and HTML which will ultimately be processed by the browser and would then be integrated into the web application [framework] or whatever.

For example, here is the UI for email application built with pyjamas. The directory consists of
franz-macbook:~/Documents/dev/playin/pyjamas-0.1/examples/mail mdfranz$ ls
AboutDialog.py Mail.html MailItem.py Mailboxes.py TopPanel.py
Contacts.py Mail.py MailItems.py Shortcuts.py build.sh
Logger.py MailDetail.py MailList.py Tasks.py output

And you run the build.sh script to start the conversion from Python to Javascript (this is calling the master Builder.py script on your python app, in this case Mail.py)
franz-macbook:~/Documents/dev/playin/pyjamas-0.1/examples/mail mdfranz$ ./build.sh
Building 'Mail.py' to output directory 'output'
Copying: public directory
Copying: Mail.html
Copying: pygwt.js
Copying: Images
Creating: Mail.nocache.html
Creating: IE6.cache.html
Importing pyjslib
Importing Mail
Importing Shortcuts
Importing Tasks
Importing ui
Importing pygwt
Importing DeferredCommand
Importing Timer
Importing History
Importing DOM
Importing Mailboxes
Importing Contacts
Importing Logger
Importing TopPanel
Importing AboutDialog
Importing Window
Importing MailList
Importing MailItems
Importing MailItem
Importing MailDetail
Creating: Opera.cache.html
Creating: OldMoz.cache.html
Creating: Safari.cache.html
Creating: Mozilla.cache.html
Done. You can run your app by opening 'output/Mail.html' in a browser
franz-macbook:~/Documents/dev/playin/pyjamas-0.1/examples/mail mdfranz$
Which then creates the following files in the output directory:
franz-macbook:~/Documents/dev/playin/pyjamas-0.1/examples/mail/output mdfranz$ ls
IE6.cache.html Mozilla.cache.html Safari.cache.html tree_open.gif
Mail.html OldMoz.cache.html pygwt.js tree_white.gif
Mail.nocache.html Opera.cache.html tree_closed.gif
As you can see different versions of HTML/Javascript are generated for respective browsers. And what you can't see (because blogger doesn't handle raw HTML very well) is the at the pyqwt.js is what kicks things off.

If this doesn't make sense there is interesting podcast by the Google GWT folks that explains the background on how they problems of JavaScript development (lack of good tools, reliance on browser hacks, browser detection, etc.) led them to come up with the solution to do development in Java and then translate the code into JavaScript.

Tuesday, March 06, 2007

Ruby, Python, and an XML-RPC Server Arbitrary Shell Command Execution Flaw

So I've been playing around with the Ruby XML-RPC APIs (and finally got them talking with Python, but more on that later) and I was sort of shocked to see that a "nasty security hole" described on the xmlrpc4r site wiki (or whatever it is) by Brian Candler is still around in the most up to date versions of Ruby 1.8.x in OpenBSD 4.0 and Ubuntu Dapper LTS. Furthermore, this particular issue does not appear to be included in any of the vulnerability databases such as NVD, CERT, or Secunia. Nor is it listed on the Ruby site. Although there is a workaround, the vulnerable code is the dominant and most common approach described in tutorials--therefore probably what is used by newbies. The "secure way" is ugly and unnatural.

Since Ruby seem to be about the only HTTP implementation that has HTTP Digest Authentication on the server side, I was thinking about using it (instead of Python, which I obviously prefer) for a small VMware monitoring app but this (not to mention Ruby/Ruby XML-RPC's relatively poor security track record and I know there have been a couple of Python issues as well) has got me reconsidering this approach. Unlike Perl, I've never had any strong opposition to using Ruby and I always considered them roughly equivalent.

The Perl-ish "there is more than one way to do it" certainly can produce less readable and could result is less secure code, both in terms of Ruby itself and apps developed with Ruby. I wouldn't want to come up with any strong conclusions on the relative insecurity of the languages or APIs, but the relative attack surface of the two XML-RPC implementations is worth a peek.

So here is a quick Python script to enumerate available methods on a XML-RPC web service that has introspection enabled.
import xmlrpclib
server = xmlrpclib.Server("")
for method in server.system.listMethods():
print method
And if we run it against the example XML-RPC server script
require "xmlrpc/server"
s = XMLRPC::Server.new
class MyHandler
def sumAndDifference(a, b)
{ "sum" => a + b, "difference" => a - b }
s.add_handler("sample", MyHandler.new)
We get a whole lot...
franz-g4:~ mdfranz$ python client.py
Compared to a simple Python server script (which actually would have its own code execution issues pre 2.4.1/2.3.5 since I didn't use dispatch)
from SimpleXMLRPCServer import *
class HeyJim(object):
def __init__(self):
def bobo(spaz):
return "x" * int(spaz)
server = SimpleXMLRPCServer(("localhost",8000))
Which only produces...
franz-g4:~ mdfranz$ python client.py
I initially used the register_function instead of register_method, but the results were the same. I wanted it to be a fair comparison. Perhaps one reaons is Ruby's method/attribute sloppiness. It exposes everything. IIRC, Python only allows methods to be exposed on objects.