Python Challenge - Level 13
- Link: http://www.pythonchallenge.com/pc/return/disproportional.html
- Username: huge
- Password: file
Problem

The image shows a telephone keypad with button "5" being clickable. Clicking it links to ../phonebook.php, which returns an XML response:
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>105</int></value>
</member>
<member>
<name>faultString</name>
<value><string>XML error: Invalid document end at line 1, column 1</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
This indicates an XML-RPC endpoint. The clue from Level 12 was "phone that evil", and evil4.jpg mentioned "Bert is evil! go back!". Therefore, we should phone Bert.
Solution
Python
Python includes xmlrpc.client in the standard library:
import xmlrpc.client
conn = xmlrpc.client.ServerProxy("http://www.pythonchallenge.com/pc/phonebook.php")
# Introspection
print(conn.system.listMethods())
# ['phone', 'system.listMethods', 'system.methodHelp', 'system.methodSignature', ...]
# Call the method
response = conn.phone("Bert")
print(response) # 555-ITALY
The returned phone number is 555-ITALY. The key word is italy.
Go
Since XML-RPC is just an HTTP POST with a standard XML envelope, we can either construct the XML payload directly using standard net/http or use an XML-RPC library:
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"regexp"
)
func main() {
xmlPayload := `<?xml version="1.0"?>
<methodCall>
<methodName>phone</methodName>
<params>
<param><value><string>Bert</string></value></param>
</params>
</methodCall>`
resp, err := http.Post(
"http://www.pythonchallenge.com/pc/phonebook.php",
"text/xml",
bytes.NewBufferString(xmlPayload),
)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
re := regexp.MustCompile(`<string>(.*?)</string>`)
matches := re.FindSubmatch(body)
if len(matches) > 1 {
fmt.Printf("Phone: %s\n", matches[1]) // 555-ITALY
}
}
Rust
In Rust, we post the XML-RPC body via ureq (or reqwest):
fn main() -> Result<(), Box<dyn std::error::Error>> {
let payload = r#"<?xml version="1.0"?>
<methodCall>
<methodName>phone</methodName>
<params>
<param><value><string>Bert</string></value></param>
</params>
</methodCall>"#;
let response = ureq::post("http://www.pythonchallenge.com/pc/phonebook.php")
.set("Content-Type", "text/xml")
.send_string(payload)?
.into_string()?;
if let Some(start) = response.find("<string>") {
if let Some(end) = response[start..].find("</string>") {
let phone = &response[start + 8..start + end];
println!("Phone: {phone}"); // 555-ITALY
}
}
Ok(())
}
Language Comparison
| Feature | Python | Go | Rust |
|---|---|---|---|
| XML-RPC Support | Built-in stdlib (xmlrpc.client) with dynamic method dispatch via ServerProxy |
No stdlib XML-RPC; simple via standard net/http POST or kolo/xmlrpc |
No stdlib; third-party crates (dxr, reqwest) or raw HTTP POST |
| RPC Abstraction | Dynamic method lookup via __getattr__ makes remote calls look like local functions |
Explicit request structs / marshaling | Explicit types with Serde deserializers |
| Network stdlib | urllib / xmlrpc.client |
Batteries-included production-grade net/http |
Minimal stdlib; network clients rely on ecosystem (reqwest, ureq) |
Key insight: Python's dynamic runtime enables ServerProxy magic, where unknown method names on the object automatically map to XML-RPC method names without pre-generated stubs. In static languages like Go and Rust, sending raw XML over standard HTTP is trivial, robust, and requires zero external RPC framework dependencies.
Next Level
Replace disproportional with italy: